summaryrefslogtreecommitdiffabout
path: root/gammu/emb/common/phone/nokia/nfunc.c
Unidiff
Diffstat (limited to 'gammu/emb/common/phone/nokia/nfunc.c') (more/less context) (ignore whitespace changes)
-rw-r--r--gammu/emb/common/phone/nokia/nfunc.c2143
1 files changed, 2143 insertions, 0 deletions
diff --git a/gammu/emb/common/phone/nokia/nfunc.c b/gammu/emb/common/phone/nokia/nfunc.c
new file mode 100644
index 0000000..3acfb10
--- a/dev/null
+++ b/gammu/emb/common/phone/nokia/nfunc.c
@@ -0,0 +1,2143 @@
1/* (c) 2002-2004 by Marcin Wiacek */
2/* based on some work from Ralf Thelen, Gabriele Zappi and MyGnokii */
3
4#include <string.h> /* memcpy only */
5#include <stdio.h>
6#include <ctype.h>
7#include <time.h>
8
9#include "../../gsmstate.h"
10#include "../../misc/coding/coding.h"
11#include "../../service/sms/gsmsms.h"
12#include "../pfunc.h"
13#include "nfunc.h"
14
15unsigned char N71_65_MEMORY_TYPES[] = {
16 MEM_DC, 0x01,
17 MEM_MC, 0x02,
18 MEM_RC, 0x03,
19 MEM_ME, 0x05,
20 MEM_SM, 0x06,
21 MEM_VM, 0x09,
22 MEM7110_SP,0x0e,
23 MEM7110_CG,0x10,
24 MEM_ON, 0x17,
25 0x00, 0x00
26};
27
28int N71_65_PackPBKBlock(GSM_StateMachine *s, int id, int size, int no, unsigned char *buf, unsigned char *block)
29{
30 smprintf(s, "Packing phonebook block with ID = %i, block number = %i, block length = %i\n",id,no+1,size+6);
31
32 block[0] = id;
33 block[1] = 0;
34 block[2] = 0;
35 block[3] = size + 6;
36 block[4] = no + 1;
37 memcpy(block+5, buf, size);
38 block[5+size] = 0;
39
40 return (size + 6);
41}
42
43int N71_65_EncodePhonebookFrame(GSM_StateMachine *s, unsigned char *req, GSM_MemoryEntry entry, int *block2, bool DCT4, bool VoiceTag)
44{
45 int count=0, len, i, block=0, j;
46 char string[500];
47 unsigned chartype;
48
49 for (i = 0; i < entry.EntriesNum; i++) {
50 type = 0;
51 if (entry.Entries[i].EntryType == PBK_Number_General) type = N7110_PBK_NUMBER_GENERAL;
52 if (entry.Entries[i].EntryType == PBK_Number_Mobile) type = N7110_PBK_NUMBER_MOBILE;
53 if (entry.Entries[i].EntryType == PBK_Number_Work) type = N7110_PBK_NUMBER_WORK;
54 if (entry.Entries[i].EntryType == PBK_Number_Fax) type = N7110_PBK_NUMBER_FAX;
55 if (entry.Entries[i].EntryType == PBK_Number_Home) type = N7110_PBK_NUMBER_HOME;
56 if (type != 0) {
57 string[0] = type;
58 len = UnicodeLength(entry.Entries[i].Text);
59
60 string[1] = 0;
61 string[2] = 0;
62
63 /* DCT 3 */
64 if (!DCT4) string[2] = entry.Entries[i].VoiceTag;
65
66 string[3] = 0;
67 string[4] = len*2+2;
68 CopyUnicodeString(string+5,entry.Entries[i].Text);
69 string[len * 2 + 5] = 0;
70 count += N71_65_PackPBKBlock(s, N7110_PBK_NUMBER, len*2+6, block++, string, req+count);
71
72 /* DCT 4 */
73 if (DCT4 && VoiceTag) {
74 block++;
75 req[count++] = N6510_PBK_VOICETAG_ID;
76 req[count++] = 0;
77 req[count++] = 0;
78 req[count++] = 8;
79 req[count++] = 0x00;
80 req[count++] = i+1;
81 req[count++] = 0x00;
82 req[count++] = entry.Entries[i].VoiceTag;
83 }
84 if (DCT4) {
85 j = 0;
86 while (entry.Entries[i].SMSList[j] != 0) {
87 string[0] = i+1;
88 string[1] = 0x00;
89 string[2] = 0x02;
90 string[3] = 0x00;
91 string[4] = entry.Entries[i].SMSList[j];
92 string[5] = 0x00;
93 count += N71_65_PackPBKBlock(s, N6510_PBK_SMSLIST_ID, 6, block++, string, req+count);
94
95 j++;
96 }
97 }
98 continue;
99 }
100 if (entry.Entries[i].EntryType == PBK_Text_Note) type = N7110_PBK_NOTE;
101 if (entry.Entries[i].EntryType == PBK_Text_Postal) type = N7110_PBK_POSTAL;
102 if (entry.Entries[i].EntryType == PBK_Text_Email) type = N7110_PBK_EMAIL;
103 if (entry.Entries[i].EntryType == PBK_Text_Email2) type = N7110_PBK_EMAIL;
104 if (entry.Entries[i].EntryType == PBK_Text_Name) type = N7110_PBK_NAME;
105 if (entry.Entries[i].EntryType == PBK_Text_URL) {
106 type = N7110_PBK_NOTE;
107 if (DCT4) type = N6510_PBK_URL;
108 }
109 if (type != 0) {
110 len = UnicodeLength(entry.Entries[i].Text);
111 string[0] = len*2+2;
112 CopyUnicodeString(string+1,entry.Entries[i].Text);
113 string[len*2+1] = 0;
114 count += N71_65_PackPBKBlock(s, type, len * 2 + 2, block++, string, req + count);
115 continue;
116
117 }
118 if (entry.Entries[i].EntryType == PBK_Caller_Group) {
119 if (!IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_PBK35)) {
120 string[0] = entry.Entries[i].Number;
121 string[1] = 0;
122 count += N71_65_PackPBKBlock(s, N7110_PBK_GROUP, 2, block++, string, req + count);
123 }
124 continue;
125 }
126 if (entry.Entries[i].EntryType == PBK_RingtoneID) {
127 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_PBK35)) {
128 string[0] = 0x00;
129 string[1] = 0x00;
130 string[2] = entry.Entries[i].Number;
131 count += N71_65_PackPBKBlock(s, N7110_PBK_RINGTONE_ID, 3, block++, string, req + count);
132 count --;
133 req[count-5] = 8;
134 }
135 continue;
136 }
137 if (entry.Entries[i].EntryType == PBK_PictureID) {
138 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_PBKIMG)) {
139 string[0] = 0x00;
140 string[1] = 0x00;
141 string[2] = 0x00;
142 string[3] = 0x00;
143 string[4] = 0x01;
144 string[5] = entry.Entries[i].Number / 256;
145 string[6] = entry.Entries[i].Number % 256;
146 string[7] = 0x00;
147 string[8] = 0x00;
148 string[9] = 0x00;
149 count += N71_65_PackPBKBlock(s, N6510_PBK_PICTURE_ID, 10, block++, string, req + count);
150 req[count-1] = 0x01;
151 }
152 continue;
153 }
154 if (entry.Entries[i].EntryType == PBK_Text_UserID) {
155 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_PBKUSER)) {
156 string[0] = UnicodeLength(entry.Entries[i].Text)*2;
157 CopyUnicodeString(string+1,entry.Entries[i].Text);
158 count += N71_65_PackPBKBlock(s, N6510_PBK_USER_ID, string[0]+2, block++, string, req+count);
159 req[count-1]--;
160 }
161 continue;
162 }
163 }
164
165 *block2=block;
166
167 return count;
168}
169
170 GSM_Error N71_65_DecodePhonebook(GSM_StateMachine*s,
171 GSM_MemoryEntry *entry,
172 GSM_Bitmap *bitmap,
173 GSM_SpeedDial *speed,
174 unsigned char *MessageBuffer,
175 int MessageLength,
176 bool DayMonthReverse)
177{
178 unsigned char *Block;
179 int length = 0, i;
180 GSM_71_65_Phonebook_Entries_TypesType;
181
182 entry->EntriesNum = 0;
183
184 if (entry->MemoryType==MEM7110_CG) {
185 bitmap->Text[0] = 0x00;
186 bitmap->Text[1] = 0x00;
187 bitmap->DefaultBitmap = true;
188 bitmap->DefaultRingtone = true;
189 }
190
191 Block = &MessageBuffer[0];
192 while (length != MessageLength) {
193#ifdef DEBUG
194 smprintf(s, "Phonebook entry block - length %i", Block[3]-6);
195 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, Block+5, Block[3]-6);
196#endif
197 if (entry->EntriesNum==GSM_PHONEBOOK_ENTRIES) {
198 smprintf(s, "Too many entries\n");
199 return ERR_UNKNOWNRESPONSE;
200 }
201
202 Type = 0;
203 if (Block[0] == N7110_PBK_NAME) {
204 Type = PBK_Text_Name; smprintf(s,"Name ");
205 }
206 if (Block[0] == N7110_PBK_EMAIL) {
207 Type = PBK_Text_Email; smprintf(s,"Email ");
208 }
209 if (Block[0] == N7110_PBK_POSTAL) {
210 Type = PBK_Text_Postal; smprintf(s,"Postal ");
211 }
212 if (Block[0] == N7110_PBK_NOTE) {
213 Type = PBK_Text_Note; smprintf(s,"Text note ");
214 }
215 if (Block[0] == N6510_PBK_URL) {
216 Type = PBK_Text_URL; smprintf(s,"URL ");
217 }
218 if (Type != 0) {
219 if (Block[5]/2>GSM_PHONEBOOK_TEXT_LENGTH) {
220 smprintf(s, "Too long text\n");
221 return ERR_UNKNOWNRESPONSE;
222 }
223 memcpy(entry->Entries[entry->EntriesNum].Text,Block+6,Block[5]);
224 entry->Entries[entry->EntriesNum].EntryType=Type;
225 smprintf(s, " \"%s\"\n",DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
226 if (Block[0] == N7110_PBK_NAME) {
227 if (entry->MemoryType==MEM7110_CG) {
228 memcpy(bitmap->Text,Block+6,Block[5]);
229 }
230 }
231 entry->EntriesNum ++;
232
233 length = length + Block[3];
234 Block = &Block[(int) Block[3]];
235 continue;
236 }
237
238 if (Block[0] == N7110_PBK_DATETIME) {
239 entry->Entries[entry->EntriesNum].EntryType=PBK_Date;
240 NOKIA_DecodeDateTime(s, Block+6, &entry->Entries[entry->EntriesNum].Date);
241 if (DayMonthReverse) {
242 i = entry->Entries[entry->EntriesNum].Date.Month;
243 entry->Entries[entry->EntriesNum].Date.Month = entry->Entries[entry->EntriesNum].Date.Day;
244 entry->Entries[entry->EntriesNum].Date.Day = i;
245 }
246 entry->EntriesNum ++;
247
248 length = length + Block[3];
249 Block = &Block[(int) Block[3]];
250 continue;
251 }
252 if (Block[0] == N6510_PBK_PICTURE_ID) {
253 entry->Entries[entry->EntriesNum].EntryType=PBK_PictureID;
254 smprintf(s, "Picture ID \"%i\"\n",Block[10]*256+Block[11]);
255 entry->Entries[entry->EntriesNum].Number=Block[10]*256+Block[11];
256 entry->EntriesNum ++;
257
258 length = length + Block[3];
259 Block = &Block[(int) Block[3]];
260 continue;
261 }
262
263 if (Block[0] == N7110_PBK_NUMBER) {
264 if (Block[5] == 0x00) {
265 Type = PBK_Number_General; smprintf(s,"General number ");
266 }
267 /* Not assigned dialed number */
268 if (Block[5] == 0x01) {
269 Type = PBK_Number_General; smprintf(s,"General number ");
270 }
271 if (Block[5] == 0x0B) {
272 Type = PBK_Number_General; smprintf(s,"General number ");
273 }
274 /* In many firmwares 0x55 visible after using
275 * Save from Call Register menu and saving number
276 * to existing phonebook entry */
277 if (Block[5] == 0x55) {
278 Type = PBK_Number_General; smprintf(s,"General number ");
279 }
280 if (Block[5] == N7110_PBK_NUMBER_GENERAL) {
281 Type = PBK_Number_General; smprintf(s,"General number ");
282 }
283 if (Block[5] == N7110_PBK_NUMBER_WORK) {
284 Type = PBK_Number_Work; smprintf(s,"Work number ");
285 }
286 if (Block[5] == N7110_PBK_NUMBER_FAX) {
287 Type = PBK_Number_Fax; smprintf(s,"Fax number ");
288 }
289 if (Block[5] == N7110_PBK_NUMBER_MOBILE) {
290 Type = PBK_Number_Mobile; smprintf(s,"Mobile number ");
291 }
292 if (Block[5] == N7110_PBK_NUMBER_HOME) {
293 Type = PBK_Number_Home; smprintf(s,"Home number ");
294 }
295 if (Type == 0x00) {
296 smprintf(s, "Unknown number type %02x\n",Block[5]);
297 return ERR_UNKNOWNRESPONSE;
298 }
299 entry->Entries[entry->EntriesNum].EntryType=Type;
300 if (Block[9]/2>GSM_PHONEBOOK_TEXT_LENGTH) {
301 smprintf(s, "Too long text\n");
302 return ERR_UNKNOWNRESPONSE;
303 }
304 memcpy(entry->Entries[entry->EntriesNum].Text,Block+10,Block[9]);
305 smprintf(s, " \"%s\"\n",DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
306 /* DCT3 phones like 6210 */
307 entry->Entries[entry->EntriesNum].VoiceTag = Block[7];
308#ifdef DEBUG
309 if (entry->Entries[entry->EntriesNum].VoiceTag != 0) smprintf(s, "Voice tag %i assigned\n",Block[7]);
310#endif
311 entry->Entries[entry->EntriesNum].SMSList[0] = 0;
312 entry->EntriesNum ++;
313
314 length = length + Block[3];
315 Block = &Block[(int) Block[3]];
316 continue;
317 }
318 if (Block[0] == N7110_PBK_RINGTONE_ID) {
319 if (entry->MemoryType==MEM7110_CG) {
320 bitmap->RingtoneID=Block[5];
321 if (Block[5] == 0x00) bitmap->RingtoneID=Block[7];
322 smprintf(s, "Ringtone ID : %i\n",bitmap->RingtoneID);
323 bitmap->DefaultRingtone = false;
324 bitmap->FileSystemRingtone = false;
325 } else {
326 entry->Entries[entry->EntriesNum].EntryType=PBK_RingtoneID;
327 smprintf(s, "Ringtone ID \"%i\"\n",Block[7]);
328 entry->Entries[entry->EntriesNum].Number=Block[7];
329 entry->EntriesNum ++;
330 }
331
332 length = length + Block[3];
333 Block = &Block[(int) Block[3]];
334 continue;
335 }
336 if (Block[0] == N7110_PBK_LOGOON) {
337 if (entry->MemoryType==MEM7110_CG) {
338 bitmap->BitmapEnabled=(Block[5]==0x00 ? false : true);
339 smprintf(s, "Logo : %s\n", bitmap->BitmapEnabled==true ? "enabled":"disabled");
340 } else {
341 return ERR_UNKNOWNRESPONSE;
342 }
343
344 length = length + Block[3];
345 Block = &Block[(int) Block[3]];
346 continue;
347 }
348 if (Block[0] == N7110_PBK_GROUPLOGO) {
349 if (entry->MemoryType==MEM7110_CG) {
350 smprintf(s, "Caller logo\n");
351 PHONE_DecodeBitmap(GSM_NokiaCallerLogo, Block+10, bitmap);
352 bitmap->DefaultBitmap = false;
353 } else {
354 return ERR_UNKNOWNRESPONSE;
355 }
356
357 length = length + Block[3];
358 Block = &Block[(int) Block[3]];
359 continue;
360 }
361 if (Block[0] == N7110_PBK_GROUP) {
362 entry->Entries[entry->EntriesNum].EntryType=PBK_Caller_Group;
363 smprintf(s, "Caller group \"%i\"\n",Block[5]);
364 entry->Entries[entry->EntriesNum].Number=Block[5];
365 if (Block[5]!=0) entry->EntriesNum ++;
366
367 length = length + Block[3];
368 Block = &Block[(int) Block[3]];
369 continue;
370 }
371 if (Block[0] == N6510_PBK_VOICETAG_ID) {
372 smprintf(s, "Entry %i has voice tag %i\n",Block[5]-1,Block[7]);
373 entry->Entries[Block[5]-1].VoiceTag = Block[7];
374
375 length = length + Block[3];
376 Block = &Block[(int) Block[3]];
377 continue;
378 }
379
380 /* 6210 5.56, SIM speed dials or ME with 1 number */
381 if (Block[0] == N7110_PBK_SIM_SPEEDDIAL) {
382 if (entry->MemoryType==MEM7110_SP) {
383#ifdef DEBUG
384 smprintf(s, "location %i\n",(Block[6]*256+Block[7]));
385 #endif
386 speed->MemoryType = MEM_ME;
387 if (Block[8] == 0x06) speed->MemoryType = MEM_SM;
388 speed->MemoryLocation = (Block[6]*256+Block[7]);
389 speed->MemoryNumberID = 2;
390 } else {
391 return ERR_UNKNOWNRESPONSE;
392 }
393
394 length = length + Block[3];
395 Block = &Block[(int) Block[3]];
396 continue;
397 }
398
399 if (Block[0] == N7110_PBK_SPEEDDIAL) {
400 if (entry->MemoryType==MEM7110_SP) {
401#ifdef DEBUG
402 switch (Block[12]) {
403 case 0x05: smprintf(s, "ME\n"); break;
404 case 0x06: smprintf(s, "SM\n"); break;
405 default : smprintf(s, "%02x\n",Block[12]);
406 }
407 smprintf(s, "location %i, number %i in location\n",
408 (Block[6]*256+Block[7])-1,Block[14]);
409 #endif
410 switch (Block[12]) {
411 case 0x05: speed->MemoryType = MEM_ME; break;
412 case 0x06: speed->MemoryType = MEM_SM; break;
413 }
414 speed->MemoryLocation = (Block[6]*256+Block[7])-1;
415 speed->MemoryNumberID = Block[14];
416 } else {
417 return ERR_UNKNOWNRESPONSE;
418 }
419
420 length = length + Block[3];
421 Block = &Block[(int) Block[3]];
422 continue;
423 }
424 if (Block[0] == N6510_PBK_RINGTONEFILE_ID) {
425 smprintf(s, "Ringtone ID with possibility of using filesystem\n");
426 if (entry->MemoryType==MEM7110_CG) {
427 if (Block[9] == 0x01) {
428 smprintf(s, "Filesystem ringtone ID: %02x\n",Block[10]*256+Block[11]);
429 bitmap->FileSystemRingtone = true;
430 } else {
431 smprintf(s, "Internal ringtone ID: %02x\n",Block[10]*256+Block[11]);
432 bitmap->FileSystemRingtone = false;
433 }
434 bitmap->RingtoneID= Block[10]*256+Block[11];
435 bitmap->DefaultRingtone = false;
436 } else {
437 return ERR_UNKNOWNRESPONSE;
438 }
439
440 length = length + Block[3];
441 Block = &Block[(int) Block[3]];
442 continue;
443 }
444 if (Block[0] == N6510_PBK_SMSLIST_ID) {
445 smprintf(s, "Entry %i is assigned to SMS list %i\n",Block[5]-1,Block[9]);
446 i = 0;
447 while(entry->Entries[Block[5]-1].SMSList[i] != 0) i++;
448 entry->Entries[Block[5]-1].SMSList[i+1] = 0;
449 entry->Entries[Block[5]-1].SMSList[i] = Block[9];
450
451 length = length + Block[3];
452 Block = &Block[(int) Block[3]];
453 continue;
454 }
455 if (Block[0] == N6510_PBK_USER_ID) {
456 smprintf(s, "User ID:");
457 entry->Entries[entry->EntriesNum].EntryType=PBK_Text_UserID;
458 if (Block[5]/2>GSM_PHONEBOOK_TEXT_LENGTH) {
459 smprintf(s, "Too long text\n");
460 return ERR_UNKNOWNRESPONSE;
461 }
462 memcpy(entry->Entries[entry->EntriesNum].Text,Block+6,Block[5]);
463 smprintf(s, " \"%s\"\n",DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
464 entry->EntriesNum ++;
465
466 length = length + Block[3];
467 Block = &Block[(int) Block[3]];
468 continue;
469 }
470 if (Block[0] == N7110_PBK_UNKNOWN1) {
471 smprintf(s,"Unknown entry\n");
472
473 length = length + Block[3];
474 Block = &Block[(int) Block[3]];
475 continue;
476 }
477
478 smprintf(s, "ERROR: unknown pbk entry 0x%02x\n",Block[0]);
479 return ERR_UNKNOWNRESPONSE;
480 }
481
482 if (entry->EntriesNum == 0) return ERR_EMPTY;
483
484 return ERR_NONE;
485}
486
487void NOKIA_GetDefaultCallerGroupName(GSM_StateMachine *s, GSM_Bitmap *Bitmap)
488{
489 Bitmap->DefaultName = false;
490 if (Bitmap->Text[0]==0x00 && Bitmap->Text[1]==0x00) {
491 Bitmap->DefaultName = true;
492 switch(Bitmap->Location) {
493 case 1: EncodeUnicode(Bitmap->Text,GetMsg(s->msg,"Family"),strlen(GetMsg(s->msg,"Family")));
494 break;
495 case 2: EncodeUnicode(Bitmap->Text,GetMsg(s->msg,"VIP"),strlen(GetMsg(s->msg,"VIP")));
496 break;
497 case 3: EncodeUnicode(Bitmap->Text,GetMsg(s->msg,"Friends"),strlen(GetMsg(s->msg,"Friends")));
498 break;
499 case 4: EncodeUnicode(Bitmap->Text,GetMsg(s->msg,"Colleagues"),strlen(GetMsg(s->msg,"Colleagues")));
500 break;
501 case 5: EncodeUnicode(Bitmap->Text,GetMsg(s->msg,"Other"),strlen(GetMsg(s->msg,"Other")));
502 break;
503 }
504 }
505}
506
507void NOKIA_DecodeDateTime(GSM_StateMachine *s, unsigned char* buffer, GSM_DateTime *datetime)
508{
509 datetime->Year= buffer[0] * 256 + buffer[1];
510 datetime->Month= buffer[2];
511 datetime->Day= buffer[3];
512
513 datetime->Hour = buffer[4];
514 datetime->Minute = buffer[5];
515 datetime->Second = buffer[6];
516
517 smprintf(s, "Decoding date and time\n");
518 smprintf(s, " Time: %02d:%02d:%02d\n",
519 datetime->Hour, datetime->Minute, datetime->Second);
520 smprintf(s, " Date: %4d/%02d/%02d\n",
521 datetime->Year, datetime->Month, datetime->Day);
522}
523
524#if defined(GSM_ENABLE_NOKIA_DCT3) || defined(GSM_ENABLE_NOKIA_DCT4)
525
526/* --------------------- Some general Nokia functions ---------------------- */
527
528void NOKIA_DecodeSMSState(GSM_StateMachine *s, unsigned char state, GSM_SMSMessage *sms)
529{
530 switch (state) {
531 case 0x01 : sms->State = SMS_Read; break;
532 case 0x03 : sms->State = SMS_UnRead; break;
533 case 0x05 : sms->State = SMS_Sent; break;
534 case 0x07 : sms->State = SMS_UnSent; break;
535 default : smprintf(s, "Unknown SMS state: %02x\n",state);
536 }
537}
538
539GSM_Error NOKIA_ReplyGetPhoneString(GSM_Protocol_Message msg, GSM_StateMachine *s)
540{
541 strcpy(s->Phone.Data.PhoneString, msg.Buffer+s->Phone.Data.StartPhoneString);
542 return ERR_NONE;
543}
544
545/* Some strings are very easy. Some header, after it required string and 0x00.
546 * We can get them using this function. We give frame to send (*string),
547 * type of message (type), pointer for buffer for response (*value), request
548 * type (request) and what is start byte in response for our string
549 */
550GSM_Error NOKIA_GetPhoneString(GSM_StateMachine *s, unsigned char *msgframe, int msglen, unsigned char msgtype, char *retvalue, GSM_Phone_RequestID request, int startresponse)
551{
552 retvalue[0] = 0;
553 s->Phone.Data.StartPhoneString = startresponse;
554 s->Phone.Data.PhoneString = retvalue;
555 return GSM_WaitFor (s, msgframe, msglen,msgtype, 4, request);
556}
557
558GSM_Error NOKIA_GetManufacturer(GSM_StateMachine *s)
559{
560 strcpy(s->Phone.Data.Manufacturer,"Nokia");
561 return ERR_NONE;
562}
563
564/* Many functions contains such strings:
565 * (1. length/256) - exist or not
566 * 2. length%256
567 * 3. string (unicode, no termination)
568 * This function read string to output and increases counter
569 */
570void NOKIA_GetUnicodeString(GSM_StateMachine *s, int *current, unsigned char *input, unsigned char *output, bool FullLength)
571{
572 int length;
573
574 if (FullLength) {
575 length = (input[*current]*256+input[*current+1])*2;
576 memcpy(output,input+(*current+2),length);
577 *current = *current + 2 + length;
578 } else {
579 length = (input[*current])*2;
580 memcpy(output,input+(*current+1),length);
581 *current = *current + 1 + length;
582 }
583
584 output[length ] = 0;
585 output[length+1] = 0;
586}
587
588int NOKIA_SetUnicodeString(GSM_StateMachine *s, unsigned char *dest, unsigned char *string, bool FullLength)
589{
590 int length;
591
592 length = UnicodeLength(string);
593 if (FullLength) {
594 dest[0] = length / 256;
595 dest[1] = length % 256;
596 CopyUnicodeString(dest + 2, string);
597 return 2+length*2;
598 } else {
599 dest[0] = length % 256;
600 CopyUnicodeString(dest + 1, string);
601 return 1+length*2;
602 }
603}
604
605/* Returns correct ID for concrete memory type */
606GSM_MemoryType NOKIA_GetMemoryType(GSM_StateMachine *s, GSM_MemoryType memory_type, unsigned char *ID)
607{
608 int i=0;
609
610 while (ID[i+1]!=0x00) {
611 if (ID[i]==memory_type) return ID[i+1];
612 i=i+2;
613 }
614 return 0xff;
615}
616
617void NOKIA_EncodeDateTime(GSM_StateMachine *s, unsigned char* buffer, GSM_DateTime *datetime)
618{
619 buffer[0] = datetime->Year / 256;
620 buffer[1] = datetime->Year % 256;
621 buffer[2] = datetime->Month;
622 buffer[3] = datetime->Day;
623
624 buffer[4] = datetime->Hour;
625 buffer[5] = datetime->Minute;
626}
627
628void NOKIA_SortSMSFolderStatus(GSM_StateMachine *s, GSM_NOKIASMSFolder *Folder)
629{
630 int i,j;
631
632 if (Folder->Number!=0) {
633 /* Bouble sorting */
634 i=0;
635 while (i!=Folder->Number-1) {
636 if (Folder->Location[i]>Folder->Location[i+1]) {
637 j=Folder->Location[i];
638 Folder->Location[i]=Folder->Location[i+1];
639 Folder->Location[i+1]=j;
640 i=0;
641 } else {
642 i++;
643 }
644 }
645#ifdef DEBUG
646 smprintf(s, "Locations: ");
647 for (i=0;i<Folder->Number;i++) {
648 smprintf(s, "%i ",Folder->Location[i]);
649 }
650 smprintf(s, "\n");
651#endif
652 }
653}
654
655void NOKIA_GetDefaultProfileName(GSM_StateMachine *s, GSM_Profile *Profile)
656{
657 if (Profile->DefaultName) {
658 switch(Profile->Location) {
659 case 1:EncodeUnicode(Profile->Name,GetMsg(s->msg,"General"),strlen(GetMsg(s->msg,"General")));
660 break;
661 case 2: EncodeUnicode(Profile->Name,GetMsg(s->msg,"Silent"),strlen(GetMsg(s->msg,"Silent")));
662 break;
663 case 3: EncodeUnicode(Profile->Name,GetMsg(s->msg,"Meeting"),strlen(GetMsg(s->msg,"Meeting")));
664 break;
665 case 4:EncodeUnicode(Profile->Name,GetMsg(s->msg,"Outdoor"),strlen(GetMsg(s->msg,"Outdoor")));
666 break;
667 case 5:EncodeUnicode(Profile->Name,GetMsg(s->msg,"Pager"),strlen(GetMsg(s->msg,"Pager")));
668 break;
669 case 6: EncodeUnicode(Profile->Name,GetMsg(s->msg,"Car"),strlen(GetMsg(s->msg,"Car")));
670 break;
671 case 7: EncodeUnicode(Profile->Name,GetMsg(s->msg,"Headset"),strlen(GetMsg(s->msg,"Headset")));
672 break;
673 }
674 }
675}
676
677/* - Shared for DCT3 (n6110.c, n7110.c, n9110.c) and DCT4 (n6510.c) phones - */
678
679GSM_Error DCT3DCT4_ReplyCallDivert(GSM_Protocol_Message msg, GSM_StateMachine *s)
680{
681 GSM_MultiCallDivert *cd = s->Phone.Data.Divert;
682 int i,pos = 11,j;
683
684 switch (msg.Buffer[3]) {
685 case 0x02:
686 smprintf(s,"Message: Call divert status received\n");
687 smprintf(s," Divert type: ");
688 switch (msg.Buffer[6]) {
689 case 0x43: smprintf(s,"when busy"); break;
690 case 0x3d: smprintf(s,"when not answered"); break;
691 case 0x3e: smprintf(s,"when phone off or no coverage");break;
692 case 0x15: smprintf(s,"all types of diverts"); break;
693 default: smprintf(s,"unknown %i",msg.Buffer[6]);break;
694 }
695 /* 6150 */
696 if (msg.Length == 0x0b) {
697 cd->Response.EntriesNum = 0;
698 return ERR_NONE;
699 }
700 cd->Response.EntriesNum = msg.Buffer[10];
701 for (i=0;i<cd->Response.EntriesNum;i++) {
702 smprintf(s,"\n Calls type : ");
703 switch (msg.Buffer[pos]) {
704 case 0x0b:
705 smprintf(s,"voice");
706 cd->Response.Entries[i].CallType = GSM_DIVERT_VoiceCalls;
707 break;
708 case 0x0d:
709 smprintf(s,"fax");
710 cd->Response.Entries[i].CallType = GSM_DIVERT_FaxCalls;
711 break;
712 case 0x19:
713 smprintf(s,"data");
714 cd->Response.Entries[i].CallType = GSM_DIVERT_DataCalls;
715 break;
716 default:
717 smprintf(s,"unknown %i",msg.Buffer[pos]);
718 /* 6310i */
719 cd->Response.EntriesNum = 0;
720 return ERR_NONE;
721 break;
722 }
723 smprintf(s,"\n");
724 j = pos + 2;
725 while (msg.Buffer[j] != 0x00) j++;
726 msg.Buffer[pos+1] = j - pos - 2;
727 GSM_UnpackSemiOctetNumber(cd->Response.Entries[i].Number,msg.Buffer+(pos+1),false);
728 smprintf(s," Number : %s\n",DecodeUnicodeString(cd->Response.Entries[i].Number));
729 cd->Response.Entries[i].Timeout = msg.Buffer[pos+34];
730 smprintf(s," Timeout : %i seconds\n",msg.Buffer[pos+34]);
731 pos+=35;
732 }
733 return ERR_NONE;
734 case 0x03:
735 smprintf(s,"Message: Call divert status receiving error ?\n");
736 return ERR_UNKNOWN;
737 }
738 return ERR_UNKNOWNRESPONSE;
739}
740
741static GSM_Error DCT3DCT4_CallDivert(GSM_StateMachine *s, GSM_MultiCallDivert *divert, bool get)
742{
743 int length = 0x09;
744 unsigned char req[55] = {N6110_FRAME_HEADER, 0x01,
745 0x05, /* operation = Query */
746 0x00,
747 0x00, /* divert type */
748 0x00, /* call type */
749 0x00};
750
751 if (!get) {
752 if (UnicodeLength(divert->Request.Number) == 0) {
753 req[4] = 0x04;
754 } else {
755 req[4] = 0x03;
756 req[8] = 0x01;
757 req[29] = GSM_PackSemiOctetNumber(divert->Request.Number, req + 9, false);
758 req[52] = divert->Request.Timeout;
759 length = 55;
760 }
761 }
762 switch (divert->Request.DivertType) {
763 case GSM_DIVERT_AllTypes : req[6] = 0x15; break;
764 case GSM_DIVERT_Busy : req[6] = 0x43; break;
765 case GSM_DIVERT_NoAnswer : req[6] = 0x3d; break;
766 case GSM_DIVERT_OutOfReach: req[6] = 0x3e; break;
767 default : return ERR_NOTIMPLEMENTED;
768 }
769
770 switch (divert->Request.CallType) {
771 case GSM_DIVERT_AllCalls : break;
772 case GSM_DIVERT_VoiceCalls: req[7] = 0x0b; break;
773 case GSM_DIVERT_FaxCalls : req[7] = 0x0d; break;
774 case GSM_DIVERT_DataCalls : req[7] = 0x19; break;
775 default : return ERR_NOTIMPLEMENTED;
776 }
777
778 s->Phone.Data.Divert = divert;
779 smprintf(s, "Call divert\n");
780 return GSM_WaitFor (s, req, length, 0x06, 10, ID_Divert);
781}
782
783GSM_Error DCT3DCT4_GetCallDivert(GSM_StateMachine *s, GSM_MultiCallDivert *divert)
784{
785 return DCT3DCT4_CallDivert(s,divert,true);
786}
787
788GSM_Error DCT3DCT4_SetCallDivert(GSM_StateMachine *s, GSM_MultiCallDivert *divert)
789{
790 return DCT3DCT4_CallDivert(s,divert,false);
791}
792
793GSM_Error DCT3DCT4_CancelAllDiverts(GSM_StateMachine *s)
794{
795 GSM_MultiCallDivert divert;
796 unsigned char req[55] = {N6110_FRAME_HEADER, 0x01,
797 0x04, /* operation = Disable */
798 0x00,
799 0x02, /* divert type */
800 0x00, /* call type */
801 0x00};
802
803 s->Phone.Data.Divert = &divert;
804 smprintf(s, "Call divert\n");
805 return GSM_WaitFor (s, req, 0x09, 0x06, 10, ID_Divert);
806}
807
808GSM_Error DCT3DCT4_ReplyGetActiveConnectSet(GSM_Protocol_Message msg, GSM_StateMachine *s)
809{
810 GSM_Phone_Data *Data = &s->Phone.Data;
811
812 Data->WAPSettings->Active = false;
813 if (Data->WAPSettings->Location - 1 == msg.Buffer[4]) {
814 Data->WAPSettings->Active = true;
815 }
816 return ERR_NONE;
817}
818
819GSM_Error DCT3DCT4_GetActiveConnectSet(GSM_StateMachine *s)
820{
821 unsigned char GetSetreq[] = {N6110_FRAME_HEADER, 0x0F};
822
823 smprintf(s, "Checking, if connection settings are active\n");
824 return GSM_WaitFor (s, GetSetreq, 4, 0x3f, 4, ID_GetConnectSet);
825}
826
827GSM_Error DCT3DCT4_ReplySetActiveConnectSet(GSM_Protocol_Message msg, GSM_StateMachine *s)
828{
829 smprintf(s, "Connection settings activated\n");
830 return ERR_NONE;
831}
832
833GSM_Error DCT3DCT4_SetActiveConnectSet(GSM_StateMachine *s, GSM_MultiWAPSettings *settings)
834{
835 unsigned charreqActivate[] = {N6110_FRAME_HEADER, 0x12,
836 0x00}; /* Location */
837
838 if (settings->Active) {
839 reqActivate[4] = settings->Location-1;
840 smprintf(s, "Activating connection settings number %i\n",settings->Location);
841 return GSM_WaitFor (s, reqActivate, 5, 0x3f, 4, ID_SetMMSSettings);
842 }
843 return ERR_NONE;
844}
845
846
847GSM_Error DCT3DCT4_SendDTMF(GSM_StateMachine *s, char *DTMFSequence)
848{
849 unsigned char req[100] = {N6110_FRAME_HEADER, 0x50,
850 0x00}; /* Length */
851
852 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_NODTMF)) return ERR_NOTSUPPORTED;
853 if (strlen(DTMFSequence) > 100 - 5) return ERR_NOTSUPPORTED;
854
855 req[4] = strlen(DTMFSequence);
856
857 memcpy(req+5,DTMFSequence,strlen(DTMFSequence));
858
859 smprintf(s, "Sending DTMF\n");
860 return GSM_WaitFor (s, req, 5+strlen(DTMFSequence), 0x01, 4, ID_SendDTMF);
861}
862
863GSM_Error DCT3DCT4_ReplyGetWAPBookmark(GSM_Protocol_Message msg, GSM_StateMachine *s, bool FullLength)
864{
865 int tmp;
866 GSM_Phone_Data *Data = &s->Phone.Data;
867
868 smprintf(s, "WAP bookmark received\n");
869 switch (msg.Buffer[3]) {
870 case 0x07:
871 tmp = 4;
872
873 Data->WAPBookmark->Location = msg.Buffer[tmp] * 256 + msg.Buffer[tmp+1];
874 smprintf(s, "Location: %i\n",Data->WAPBookmark->Location);
875 tmp = tmp + 2;
876
877 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer, Data->WAPBookmark->Title, FullLength);
878 smprintf(s, "Title : \"%s\"\n",DecodeUnicodeString(Data->WAPBookmark->Title));
879
880 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer, Data->WAPBookmark->Address, FullLength);
881 smprintf(s, "Address : \"%s\"\n",DecodeUnicodeString(Data->WAPBookmark->Address));
882
883 return ERR_NONE;
884 case 0x08:
885 switch (msg.Buffer[4]) {
886 case 0x01:
887 smprintf(s, "Security error. Inside WAP bookmarks menu\n");
888 return ERR_INSIDEPHONEMENU;
889 case 0x02:
890 smprintf(s, "Invalid or empty\n");
891 return ERR_INVALIDLOCATION;
892 default:
893 smprintf(s, "ERROR: unknown %i\n",msg.Buffer[4]);
894 return ERR_UNKNOWNRESPONSE;
895 }
896 break;
897 }
898 return ERR_UNKNOWNRESPONSE;
899}
900
901GSM_Error DCT3DCT4_ReplySetWAPBookmark(GSM_Protocol_Message msg, GSM_StateMachine *s)
902{
903 switch (msg.Buffer[3]) {
904 case 0x0A:
905 smprintf(s, "WAP bookmark set OK\n");
906 return ERR_NONE;
907 case 0x0B:
908 smprintf(s, "WAP bookmark setting error\n");
909 switch (msg.Buffer[4]) {
910 case 0x01:
911 smprintf(s, "Security error. Inside WAP bookmarks menu\n");
912 return ERR_INSIDEPHONEMENU;
913 case 0x02:
914 smprintf(s, "Can't write to empty location ?\n");
915 return ERR_EMPTY;
916 case 0x04:
917 smprintf(s, "Full memory\n");
918 return ERR_FULL;
919 default:
920 smprintf(s, "ERROR: unknown %i\n",msg.Buffer[4]);
921 return ERR_UNKNOWNRESPONSE;
922 }
923 }
924 return ERR_UNKNOWNRESPONSE;
925}
926
927GSM_Error DCT3DCT4_ReplyEnableConnectFunc(GSM_Protocol_Message msg, GSM_StateMachine *s)
928{
929 smprintf(s, "Connection functions enabled\n");
930 return ERR_NONE;
931}
932
933GSM_Error DCT3DCT4_EnableWAPFunctions(GSM_StateMachine *s)
934{
935 unsigned char req[] = {N6110_FRAME_HEADER, 0x00};
936
937 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo,F_NOWAP)) return ERR_NOTSUPPORTED;
938
939 smprintf(s, "Enabling WAP\n");
940 return GSM_WaitFor (s, req, 4, 0x3f, 4, ID_EnableConnectFunc);
941}
942
943GSM_Error DCT3DCT4_ReplyDisableConnectFunc(GSM_Protocol_Message msg, GSM_StateMachine *s)
944{
945 smprintf(s, "Connection functions disabled\n");
946 return ERR_NONE;
947}
948
949GSM_Error DCT3DCT4_DisableConnectionFunctions(GSM_StateMachine *s)
950{
951 unsigned char req[] = {N6110_FRAME_HEADER, 0x03};
952
953 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo,F_NOWAP)) return ERR_NOTSUPPORTED;
954
955 smprintf(s, "Disabling connection settings\n");
956 return GSM_WaitFor (s, req, 4, 0x3f, 4, ID_DisableConnectFunc);
957}
958
959GSM_Error DCT3DCT4_ReplyDelWAPBookmark(GSM_Protocol_Message msg, GSM_StateMachine *s)
960{
961 switch (msg.Buffer[3]) {
962 case 0x0D:
963 smprintf(s, "WAP bookmark deleted OK\n");
964 return ERR_NONE;
965 case 0x0E:
966 smprintf(s, "WAP bookmark deleting error\n");
967 switch (msg.Buffer[4]) {
968 case 0x01:
969 smprintf(s, "Security error. Inside WAP bookmarks menu\n");
970 return ERR_SECURITYERROR;
971 case 0x02:
972 smprintf(s, "Invalid location\n");
973 return ERR_INVALIDLOCATION;
974 default:
975 smprintf(s, "ERROR: unknown %i\n",msg.Buffer[4]);
976 return ERR_UNKNOWNRESPONSE;
977 }
978 }
979 return ERR_UNKNOWNRESPONSE;
980}
981
982GSM_Error DCT3DCT4_DeleteWAPBookmarkPart(GSM_StateMachine *s, GSM_WAPBookmark *bookmark)
983{
984 GSM_Errorerror;
985 unsigned char req[] = {N6110_FRAME_HEADER, 0x0C,
986 0x00, 0x00}; /* Location */
987
988 req[5] = bookmark->Location;
989
990 smprintf(s, "Deleting WAP bookmark\n");
991 error = GSM_WaitFor (s, req, 6, 0x3f, 4, ID_DeleteWAPBookmark);
992 if (error != ERR_NONE) {
993 if (error == ERR_INVALIDLOCATION || error == ERR_INSIDEPHONEMENU) {
994 DCT3DCT4_DisableConnectionFunctions(s);
995 }
996 return error;
997 }
998
999 return DCT3DCT4_DisableConnectionFunctions(s);
1000}
1001
1002GSM_Error DCT3DCT4_GetWAPBookmarkPart(GSM_StateMachine *s, GSM_WAPBookmark *bookmark)
1003{
1004 GSM_Errorerror;
1005 unsigned char req[] = {N6110_FRAME_HEADER, 0x06,
1006 0x00, 0x00}; /* Location */
1007
1008 req[5]=bookmark->Location-1;
1009
1010 s->Phone.Data.WAPBookmark=bookmark;
1011 smprintf(s, "Getting WAP bookmark\n");
1012 error = GSM_WaitFor (s, req, 6, 0x3f, 4, ID_GetWAPBookmark);
1013 if (error != ERR_NONE) {
1014 if (error == ERR_INVALIDLOCATION || error == ERR_INSIDEPHONEMENU) {
1015 DCT3DCT4_DisableConnectionFunctions(s);
1016 }
1017 return error;
1018 }
1019
1020 return DCT3DCT4_DisableConnectionFunctions(s);
1021}
1022
1023GSM_Error DCT3DCT4_CancelCall(GSM_StateMachine *s, int ID)
1024{
1025 unsigned char req[] = {N6110_FRAME_HEADER, 0x08, 0x00, 0x85};
1026
1027 req[4] = (unsigned char)ID;
1028 s->Phone.Data.CallID = ID;
1029
1030 smprintf(s, "Canceling single call\n");
1031 return GSM_WaitFor (s, req, 6, 0x01, 4, ID_CancelCall);
1032}
1033
1034GSM_Error DCT3DCT4_AnswerCall(GSM_StateMachine *s, int ID)
1035{
1036 unsigned char req[] = {N6110_FRAME_HEADER, 0x06, 0x00, 0x00};
1037
1038 req[4] = (unsigned char)ID;
1039 s->Phone.Data.CallID = ID;
1040
1041 smprintf(s, "Answering single call\n");
1042 return GSM_WaitFor (s, req, 6, 0x01, 4, ID_AnswerCall);
1043}
1044
1045GSM_Error DCT3DCT4_ReplyGetModelFirmware(GSM_Protocol_Message msg, GSM_StateMachine *s)
1046{
1047 GSM_Lineslines;
1048 GSM_Phone_Data*Data = &s->Phone.Data;
1049
1050 SplitLines(msg.Buffer, msg.Length, &lines, "\x20\x0A", 2, false);
1051
1052 strcpy(Data->Model,GetLineString(msg.Buffer, lines, 4));
1053 smprintf(s, "Received model %s\n",Data->Model);
1054 Data->ModelInfo = GetModelData(NULL,Data->Model,NULL);
1055
1056 strcpy(Data->VerDate,GetLineString(msg.Buffer, lines, 3));
1057 smprintf(s, "Received firmware date %s\n",Data->VerDate);
1058
1059 strcpy(Data->Version,GetLineString(msg.Buffer, lines, 2));
1060 smprintf(s, "Received firmware version %s\n",Data->Version);
1061 GSM_CreateFirmwareNumber(s);
1062
1063 return ERR_NONE;
1064}
1065
1066GSM_Error DCT3DCT4_GetModel (GSM_StateMachine *s)
1067{
1068 unsigned char req[5] = {N6110_FRAME_HEADER, 0x03, 0x00};
1069 GSM_Error error;
1070
1071 if (strlen(s->Phone.Data.Model)>0) return ERR_NONE;
1072
1073 smprintf(s, "Getting model\n");
1074 error=GSM_WaitFor (s, req, 5, 0xd1, 3, ID_GetModel);
1075 if (error==ERR_NONE) {
1076 if (s->di.dl==DL_TEXT || s->di.dl==DL_TEXTALL ||
1077 s->di.dl==DL_TEXTDATE || s->di.dl==DL_TEXTALLDATE) {
1078 smprintf(s, "[Connected model - \"%s\"]\n",s->Phone.Data.Model);
1079 smprintf(s, "[Firmware version - \"%s\"]\n",s->Phone.Data.Version);
1080 smprintf(s, "[Firmware date - \"%s\"]\n",s->Phone.Data.VerDate);
1081 }
1082 }
1083 return error;
1084}
1085
1086GSM_Error DCT3DCT4_GetFirmware (GSM_StateMachine *s)
1087{
1088 unsigned char req[5] = {N6110_FRAME_HEADER, 0x03, 0x00};
1089 GSM_Error error;
1090
1091 if (strlen(s->Phone.Data.Version)>0) return ERR_NONE;
1092
1093 smprintf(s, "Getting firmware version\n");
1094 error=GSM_WaitFor (s, req, 5, 0xd1, 3, ID_GetFirmware);
1095 if (error==ERR_NONE) {
1096 if (s->di.dl==DL_TEXT || s->di.dl==DL_TEXTALL ||
1097 s->di.dl==DL_TEXTDATE || s->di.dl==DL_TEXTALLDATE) {
1098 smprintf(s, "[Connected model - \"%s\"]\n",s->Phone.Data.Model);
1099 smprintf(s, "[Firmware version - \"%s\"]\n",s->Phone.Data.Version);
1100 smprintf(s, "[Firmware date - \"%s\"]\n",s->Phone.Data.VerDate);
1101 }
1102 }
1103 return error;
1104}
1105
1106/* ---------- Shared for n7110.c and n6510.c ------------------------------- */
1107
1108GSM_Error N71_65_ReplyGetMemoryError(unsigned char error, GSM_StateMachine *s)
1109{
1110 switch (error) {
1111 case 0x27:
1112 smprintf(s, "No PIN\n");
1113 return ERR_SECURITYERROR;
1114 case 0x30:
1115 smprintf(s, "Invalid memory type\n");
1116 if (s->Phone.Data.Memory->MemoryType == MEM_ME) return ERR_EMPTY;
1117 if (s->Phone.Data.Memory->MemoryType == MEM_SM) return ERR_EMPTY;
1118 return ERR_NOTSUPPORTED;
1119 case 0x33:
1120 smprintf(s, "Empty location\n");
1121 s->Phone.Data.Memory->EntriesNum = 0;
1122 return ERR_EMPTY;
1123 case 0x34:
1124 smprintf(s, "Too high location ?\n");
1125 return ERR_INVALIDLOCATION;
1126 default:
1127 smprintf(s, "ERROR: unknown %i\n",error);
1128 return ERR_UNKNOWNRESPONSE;
1129 }
1130}
1131
1132GSM_Error N71_65_ReplyWritePhonebook(GSM_Protocol_Message msg, GSM_StateMachine *s)
1133{
1134 smprintf(s, "Phonebook entry written ");
1135 switch (msg.Buffer[6]) {
1136 case 0x0f:
1137 smprintf(s, " - error\n");
1138 switch (msg.Buffer[10]) {
1139 case 0x36:
1140 smprintf(s, "Too long name\n");
1141 return ERR_NOTSUPPORTED;
1142 case 0x3c:
1143 smprintf(s, "Can not add entry with 0 subentries\n");
1144 return ERR_NOTSUPPORTED;
1145 case 0x3d:
1146 smprintf(s, "Wrong entry type\n");
1147 return ERR_NOTSUPPORTED;
1148 case 0x3e:
1149 smprintf(s, "Too much entries\n");
1150 return ERR_NOTSUPPORTED;
1151 default:
1152 smprintf(s, "ERROR: unknown %i\n",msg.Buffer[10]);
1153 return ERR_UNKNOWNRESPONSE;
1154 }
1155 default:
1156 smprintf(s, " - OK\n");
1157 return ERR_NONE;
1158 }
1159}
1160
1161bool NOKIA_FindPhoneFeatureValue(GSM_StateMachine *s,
1162 GSM_Profile_PhoneTableValue ProfileTable[],
1163 GSM_Profile_Feat_ID FeatureID,
1164 GSM_Profile_Feat_Value FeatureValue,
1165 unsigned char *PhoneID,
1166 unsigned char *PhoneValue)
1167{
1168 int i=0;
1169
1170 smprintf(s, "Trying to find feature %i with value %i\n",FeatureID,FeatureValue);
1171 while (ProfileTable[i].ID != 0x00) {
1172 if (ProfileTable[i].ID == FeatureID &&
1173 ProfileTable[i].Value == FeatureValue) {
1174 *PhoneID= ProfileTable[i].PhoneID;
1175 *PhoneValue= ProfileTable[i].PhoneValue;
1176 return true;
1177 }
1178 i++;
1179 }
1180 return false;
1181}
1182
1183#define PROFILE_CALLERGROUPS_GROUP1 0x01
1184#define PROFILE_CALLERGROUPS_GROUP2 0x02
1185#define PROFILE_CALLERGROUPS_GROUP3 0x04
1186#define PROFILE_CALLERGROUPS_GROUP4 0x08
1187#define PROFILE_CALLERGROUPS_GROUP5 0x10
1188
1189 void NOKIA_FindFeatureValue(GSM_StateMachine *s,
1190 GSM_Profile_PhoneTableValue ProfileTable[],
1191 unsigned char ID,
1192 unsigned char Value,
1193 GSM_Phone_Data *Data,
1194 bool CallerGroups)
1195{
1196 int i;
1197
1198 if (CallerGroups) {
1199 smprintf(s, "Caller groups: %i\n", Value);
1200 Data->Profile->FeatureID [Data->Profile->FeaturesNumber] = Profile_CallerGroups;
1201 Data->Profile->FeaturesNumber++;
1202 for (i=0;i<5;i++) Data->Profile->CallerGroups[i] = false;
1203 if ((Value & PROFILE_CALLERGROUPS_GROUP1)==PROFILE_CALLERGROUPS_GROUP1) Data->Profile->CallerGroups[0] = true;
1204 if ((Value & PROFILE_CALLERGROUPS_GROUP2)==PROFILE_CALLERGROUPS_GROUP2) Data->Profile->CallerGroups[1] = true;
1205 if ((Value & PROFILE_CALLERGROUPS_GROUP3)==PROFILE_CALLERGROUPS_GROUP3) Data->Profile->CallerGroups[2] = true;
1206 if ((Value & PROFILE_CALLERGROUPS_GROUP4)==PROFILE_CALLERGROUPS_GROUP4) Data->Profile->CallerGroups[3] = true;
1207 if ((Value & PROFILE_CALLERGROUPS_GROUP5)==PROFILE_CALLERGROUPS_GROUP5) Data->Profile->CallerGroups[4] = true;
1208 return;
1209 }
1210
1211 i = 0;
1212 while (ProfileTable[i].ID != 0x00) {
1213 if (ProfileTable[i].PhoneID == ID &&
1214 ProfileTable[i].PhoneValue == Value) {
1215#ifdef DEBUG
1216 switch (ProfileTable[i].ID) {
1217 case Profile_KeypadTone : smprintf(s, "Keypad tones\n"); break;
1218 case Profile_CallAlert : smprintf(s, "Call alert\n"); break;
1219 case Profile_RingtoneVolume : smprintf(s, "Ringtone volume\n"); break;
1220 case Profile_MessageTone : smprintf(s, "SMS message tones\n"); break;
1221 case Profile_Vibration : smprintf(s, "Vibration\n"); break;
1222 case Profile_WarningTone: smprintf(s, "Warning (ang games) tones\n"); break;
1223 case Profile_AutoAnswer : smprintf(s, "Automatic answer\n"); break;
1224 case Profile_Lights : smprintf(s, "Lights\n"); break;
1225 case Profile_ScreenSaver : smprintf(s, "Screen Saver\n"); break;
1226 case Profile_ScreenSaverTime : smprintf(s, "Screen Saver timeout\n"); break;
1227 default : break;
1228 }
1229#endif
1230 Data->Profile->FeatureID[Data->Profile->FeaturesNumber] = ProfileTable[i].ID;
1231 Data->Profile->FeatureValue[Data->Profile->FeaturesNumber] = ProfileTable[i].Value;
1232 Data->Profile->FeaturesNumber++;
1233 break;
1234 }
1235 i++;
1236 }
1237}
1238
1239GSM_Profile_PhoneTableValue Profile71_65[] = {
1240 {Profile_KeypadTone, PROFILE_KEYPAD_OFF, 0x00,0x00},
1241 {Profile_KeypadTone, PROFILE_KEYPAD_LEVEL1, 0x00,0x01},
1242 {Profile_KeypadTone, PROFILE_KEYPAD_LEVEL2, 0x00,0x02},
1243 {Profile_KeypadTone, PROFILE_KEYPAD_LEVEL3, 0x00,0x03},
1244 /* Lights ? */
1245 {Profile_CallAlert, PROFILE_CALLALERT_RINGING,0x02,0x00},
1246 {Profile_CallAlert, PROFILE_CALLALERT_ASCENDING,0x02,0x01},
1247 {Profile_CallAlert, PROFILE_CALLALERT_RINGONCE,0x02,0x02},
1248 {Profile_CallAlert, PROFILE_CALLALERT_BEEPONCE,0x02,0x03},
1249 {Profile_CallAlert, PROFILE_CALLALERT_OFF, 0x02,0x05},
1250 /* {Profile_CallAlert, PROFILE_CALLALERT_CALLERGROUPS,0x02,0x07},*/
1251 /* Ringtone ID */
1252 {Profile_RingtoneVolume, PROFILE_VOLUME_LEVEL1, 0x04,0x00},
1253 {Profile_RingtoneVolume, PROFILE_VOLUME_LEVEL2, 0x04,0x01},
1254 {Profile_RingtoneVolume, PROFILE_VOLUME_LEVEL3, 0x04,0x02},
1255 {Profile_RingtoneVolume, PROFILE_VOLUME_LEVEL4, 0x04,0x03},
1256 {Profile_RingtoneVolume, PROFILE_VOLUME_LEVEL5, 0x04,0x04},
1257 {Profile_MessageTone, PROFILE_MESSAGE_NOTONE,0x05,0x00},
1258 {Profile_MessageTone, PROFILE_MESSAGE_STANDARD,0x05,0x01},
1259 {Profile_MessageTone, PROFILE_MESSAGE_SPECIAL,0x05,0x02},
1260 {Profile_MessageTone, PROFILE_MESSAGE_BEEPONCE,0x05,0x03},
1261 {Profile_MessageTone, PROFILE_MESSAGE_ASCENDING,0x05,0x04},
1262 {Profile_Vibration, PROFILE_VIBRATION_OFF, 0x06,0x00},
1263 {Profile_Vibration, PROFILE_VIBRATION_ON, 0x06,0x01},
1264 {Profile_WarningTone, PROFILE_WARNING_OFF, 0x07,0x00},
1265 {Profile_WarningTone, PROFILE_WARNING_ON, 0x07,0x01},
1266 /* Caller groups */
1267 {Profile_AutoAnswer, PROFILE_AUTOANSWER_OFF,0x09,0x00},
1268 {Profile_AutoAnswer, PROFILE_AUTOANSWER_ON, 0x09,0x01},
1269 {0x00, 0x00, 0x00,0x00}
1270};
1271
1272GSM_Error NOKIA_SetIncomingSMS(GSM_StateMachine *s, bool enable)
1273{
1274 s->Phone.Data.EnableIncomingSMS = enable;
1275#ifdef DEBUG
1276 if (enable) {
1277 smprintf(s, "Enabling incoming SMS\n");
1278 } else {
1279 smprintf(s, "Disabling incoming SMS\n");
1280 }
1281#endif
1282 return ERR_NONE;
1283}
1284
1285GSM_Error N71_65_ReplyUSSDInfo(GSM_Protocol_Message msg, GSM_StateMachine *s)
1286{
1287 unsigned char buffer[2000],buffer2[4000];
1288
1289 if (s->Phone.Data.RequestID == ID_Divert) return ERR_NONE;
1290
1291 memcpy(buffer,msg.Buffer+8,msg.Buffer[7]);
1292 buffer[msg.Buffer[7]] = 0x00;
1293
1294 smprintf(s, "USSD reply: \"%s\"\n",buffer);
1295
1296 if (s->Phone.Data.EnableIncomingUSSD && s->User.IncomingUSSD!=NULL) {
1297 EncodeUnicode(buffer2,buffer,strlen(buffer));
1298 s->User.IncomingUSSD(s->CurrentConfig->Device, buffer2);
1299 }
1300
1301 return ERR_NONE;
1302}
1303
1304GSM_Error NOKIA_SetIncomingUSSD(GSM_StateMachine *s, bool enable)
1305{
1306 s->Phone.Data.EnableIncomingUSSD = enable;
1307#ifdef DEBUG
1308 if (enable) {
1309 smprintf(s, "Enabling incoming USSD\n");
1310 } else {
1311 smprintf(s, "Disabling incoming USSD\n");
1312 }
1313#endif
1314 return ERR_NONE;
1315}
1316
1317GSM_Error NOKIA_SetIncomingCall(GSM_StateMachine *s, bool enable)
1318{
1319 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo,F_NOCALLINFO)) return ERR_NOTSUPPORTED;
1320
1321 s->Phone.Data.EnableIncomingCall = enable;
1322#ifdef DEBUG
1323 if (enable) {
1324 smprintf(s, "Enabling incoming Call\n");
1325 } else {
1326 smprintf(s, "Disabling incoming Call\n");
1327 }
1328#endif
1329 return ERR_NONE;
1330}
1331
1332GSM_Error N71_65_ReplyCallInfo(GSM_Protocol_Message msg, GSM_StateMachine *s)
1333{
1334 GSM_Call call;
1335 int tmp;
1336 unsigned char buffer[200];
1337
1338 call.Status = 0;
1339 call.CallIDAvailable = true;
1340 smprintf(s, "Call info, ");
1341 switch (msg.Buffer[3]) {
1342 case 0x02:
1343 smprintf(s, "Call established, waiting for answer\n");
1344 call.Status = GSM_CALL_CallEstablished;
1345 break;
1346 case 0x03:
1347 smprintf(s, "Call started\n");
1348 smprintf(s, "Call mode : %i\n",msg.Buffer[5]);//such interpretation is in gnokii
1349 tmp = 6;
1350 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer,buffer,false);
1351 smprintf(s, "Number : \"%s\"\n",DecodeUnicodeString(buffer));
1352 /* FIXME: read name from frame */
1353
1354 call.Status = GSM_CALL_CallStart;
1355 break;
1356 case 0x04:
1357 smprintf(s, "Remote end hang up\n");
1358 smprintf(s, "Cause Type : %i\n",msg.Buffer[5]);//such interpretation is in gnokii
1359 smprintf(s, "CC : %i\n",msg.Buffer[6]);
1360 smprintf(s, "MM(?) : %i\n",msg.Buffer[7]);
1361 smprintf(s, "RR(?) : %i\n",msg.Buffer[8]);
1362 call.Status = GSM_CALL_CallRemoteEnd;
1363 call.StatusCode = msg.Buffer[6];
1364 break;
1365 case 0x05:
1366 smprintf(s, "Incoming call\n");
1367 smprintf(s, "Call mode : %i\n",msg.Buffer[5]);//such interpretation is in gnokii
1368 tmp = 6;
1369 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer,buffer,false);
1370 smprintf(s, "Number : \"%s\"\n",DecodeUnicodeString(buffer));
1371 /* FIXME: read name from frame */
1372 call.Status = GSM_CALL_IncomingCall;
1373 tmp = 6;
1374 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer,call.PhoneNumber,false);
1375 break;
1376 case 0x07:
1377 smprintf(s, "Call answer initiated\n");
1378 break;
1379 case 0x09:
1380 smprintf(s, "Call released\n");
1381 call.Status = GSM_CALL_CallLocalEnd;
1382 break;
1383 case 0x0a:
1384 smprintf(s, "Call is being released\n");
1385 break;
1386 case 0x0b:
1387 smprintf(s, "Meaning not known\n");
1388 call.CallIDAvailable = false;
1389 break;
1390 case 0x0c:
1391 smprintf(s, "Audio status\n");
1392 if (msg.Buffer[4] == 0x01) smprintf(s, "Audio enabled\n");
1393 else smprintf(s, "Audio disabled\n");
1394 call.CallIDAvailable = false;
1395 break;
1396 case 0x23:
1397 smprintf(s, "Call held\n");
1398 call.Status = GSM_CALL_CallHeld;
1399 break;
1400 case 0x25:
1401 smprintf(s, "Call resumed\n");
1402 call.Status = GSM_CALL_CallResumed;
1403 break;
1404 case 0x27:
1405 smprintf(s, "Call switched\n");
1406 call.Status = GSM_CALL_CallSwitched;
1407 break;
1408 case 0x53:
1409 smprintf(s, "Outgoing call\n");
1410 smprintf(s, "Call mode : %i\n",msg.Buffer[5]);//such interpretation is in gnokii
1411 tmp = 6;
1412 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer,buffer,false);
1413 smprintf(s, "Number : \"%s\"\n",DecodeUnicodeString(buffer));
1414 /* FIXME: read name from frame */
1415 call.Status = GSM_CALL_OutgoingCall;
1416 tmp = 6;
1417 NOKIA_GetUnicodeString(s, &tmp, msg.Buffer,call.PhoneNumber,false);
1418 break;
1419 }
1420 if (call.CallIDAvailable) smprintf(s, "Call ID : %d\n",msg.Buffer[4]);
1421 if (s->Phone.Data.EnableIncomingCall && s->User.IncomingCall!=NULL && call.Status != 0) {
1422 if (call.CallIDAvailable) call.CallID = msg.Buffer[4];
1423 s->User.IncomingCall(s->CurrentConfig->Device, call);
1424 }
1425 if (s->Phone.Data.RequestID == ID_CancelCall) {
1426 if (msg.Buffer[3] == 0x09) {
1427 if (s->Phone.Data.CallID == msg.Buffer[4]) return ERR_NONE;
1428 /* when we canceled call and see frame about other
1429 * call releasing, we don't give ERR_NONE for "our"
1430 * call release command
1431 */
1432 return ERR_NEEDANOTHERANSWER;
1433 }
1434 }
1435 if (s->Phone.Data.RequestID == ID_AnswerCall) {
1436 if (msg.Buffer[3] == 0x07) {
1437 if (s->Phone.Data.CallID == msg.Buffer[4]) return ERR_NONE;
1438 return ERR_NEEDANOTHERANSWER;
1439 }
1440 }
1441 return ERR_NONE;
1442}
1443
1444void N71_65_GetCalendarRecurrance(GSM_StateMachine *s, unsigned char *buffer, GSM_CalendarEntry *entry)
1445{
1446 int Recurrance;
1447
1448 Recurrance = buffer[0]*256 + buffer[1];
1449 /* 8760 hours = 1 year */
1450 if (Recurrance == 0xffff) Recurrance=8760;
1451 if (Recurrance != 0) {
1452 smprintf(s, "Recurrance : %i hours\n",Recurrance);
1453 entry->Entries[entry->EntriesNum].EntryType= CAL_RECURRANCE;
1454 entry->Entries[entry->EntriesNum].Number= Recurrance;
1455 entry->EntriesNum++;
1456 }
1457}
1458
1459/* method 2 */
1460GSM_Error N71_65_ReplyAddCalendar2(GSM_Protocol_Message msg, GSM_StateMachine *s)
1461{
1462 smprintf(s, "Calendar note added\n");
1463 return ERR_NONE;
1464}
1465
1466/* method 2 */
1467GSM_Error N71_65_AddCalendar2(GSM_StateMachine *s, GSM_CalendarEntry *Note)
1468{
1469 GSM_CalendarNoteTypeNoteType;
1470 time_t t_time1,t_time2;
1471 GSM_DateTime Date,date_time;
1472 GSM_Error error;
1473 long diff;
1474 int Text, Time, Alarm, Phone, Recurrance, EndTime, Location, length=25;
1475 unsigned char req[5000] = {
1476 N6110_FRAME_HEADER,
1477 0x40,
1478 0x00, /* frame length - 7 */
1479 0x00,0x00,0x00,0x00,
1480 0x00,0x00,0x00,0x00, /* start time saved as difference */
1481 0x00,0x00,0xff,0xff, /* alarm saved as difference */
1482 0x00, /* frame length - 7 */
1483 0x00, /* note type */
1484 0x00,0x00, /* recurrance */
1485 0x00,0x00,0x00,0x00,
1486 0x00,0x00,0x00,0x00}; /* rest depends on note type */
1487
1488 NoteType = N71_65_FindCalendarType(Note->Type, s->Phone.Data.ModelInfo);
1489
1490 if (IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_CAL62) ||
1491 IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_CAL65)) {
1492 switch(NoteType) {
1493 case GSM_CAL_MEETING : req[18] = 0x01; length = 25; break;
1494 case GSM_CAL_CALL : req[18] = 0x02; length = 27; break;
1495 case GSM_CAL_BIRTHDAY: req[18] = 0x04; length = 28; break;
1496 case GSM_CAL_MEMO : req[18] = 0x08; length = 25; break;
1497 default : return ERR_UNKNOWN;
1498 }
1499 } else {
1500 switch(NoteType) {
1501 case GSM_CAL_REMINDER: req[18] = 0x01; length = 25; break;
1502 case GSM_CAL_CALL : req[18] = 0x02; length = 27; break;
1503 case GSM_CAL_BIRTHDAY: req[18] = 0x04; length = 28; break;
1504 case GSM_CAL_MEMO : req[18] = 0x08; length = 25; break;
1505 default : return ERR_UNKNOWN;
1506 }
1507 }
1508
1509 GSM_CalendarFindDefaultTextTimeAlarmPhoneRecurrance(Note, &Text, &Time, &Alarm, &Phone, &Recurrance, &EndTime, &Location);
1510
1511 if (Time == -1) return ERR_UNKNOWN;
1512 if (NoteType != GSM_CAL_BIRTHDAY) {
1513 Date.Year = 2030; Date.Month = 01; Date.Day = 01;
1514 Date.Hour = 00; Date.Minute = 00; Date.Second = 00;
1515 } else {
1516 Date.Year = 2029; Date.Month = 12; Date.Day = 31;
1517 Date.Hour = 22; Date.Minute = 59; Date.Second = 58;
1518 }
1519 t_time1 = Fill_Time_T(Date,8);
1520 memcpy(&Date,&Note->Entries[Time].Date,sizeof(GSM_DateTime));
1521 if (NoteType != GSM_CAL_BIRTHDAY) {
1522 Date.Year -= 20;
1523 } else {
1524 Date.Year = 1980;
1525 Date.Hour = 22; Date.Minute = 58; Date.Second = 58;
1526 }
1527 t_time2 = Fill_Time_T(Date,8);
1528 diff= t_time1-t_time2;
1529 smprintf(s, " Difference : %li seconds\n", -diff);
1530 req[9] = (unsigned char)(-diff >> 24);
1531 req[10] = (unsigned char)(-diff >> 16);
1532 req[11] = (unsigned char)(-diff >> 8);
1533 req[12] = (unsigned char)(-diff);
1534 if (NoteType == GSM_CAL_BIRTHDAY) {
1535 req[25] = Note->Entries[Time].Date.Year / 256;
1536 req[26] = Note->Entries[Time].Date.Year % 256;
1537 /* Recurrance = 1 year */
1538 req[19] = 0xff;
1539 req[20] = 0xff;
1540 }
1541
1542 if (NoteType == GSM_CAL_CALL && Phone != -1) {
1543 req[25] = UnicodeLength(Note->Entries[Phone].Text);
1544 CopyUnicodeString(req+length,Note->Entries[Phone].Text);
1545 length += UnicodeLength(Note->Entries[Phone].Text)*2;
1546 }
1547
1548 if (Alarm != -1) {
1549 if (NoteType == GSM_CAL_BIRTHDAY) {
1550 if (Note->Entries[Alarm].EntryType == CAL_SILENT_ALARM_DATETIME) req[27] = 0x01;
1551 error=s->Phone.Functions->GetDateTime(s,&date_time);
1552 switch (error) {
1553 case ERR_EMPTY:
1554 case ERR_NOTIMPLEMENTED:
1555 GSM_GetCurrentDateTime(&date_time);
1556 break;
1557 case ERR_NONE:
1558 break;
1559 default:
1560 return error;
1561 }
1562 Date.Year= date_time.Year;
1563 Date.Hour = 23;
1564 Date.Minute = 59;
1565 } else {
1566 Date.Year += 20;
1567 }
1568 t_time2 = Fill_Time_T(Date,8);
1569 t_time1 = Fill_Time_T(Note->Entries[Alarm].Date,8);
1570 diff = t_time1-t_time2;
1571
1572 /* Sometimes we have difference in minutes */
1573 if (NoteType == GSM_CAL_MEETING) diff = diff / 60;
1574 if (!IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_CAL35)) {
1575 if (NoteType == GSM_CAL_MEMO || NoteType == GSM_CAL_CALL) {
1576 diff = diff / 60;
1577 }
1578 }
1579
1580 smprintf(s, " Difference : %li seconds or minutes\n", -diff);
1581 req[13] = (unsigned char)(-diff >> 24);
1582 req[14] = (unsigned char)(-diff >> 16);
1583 req[15] = (unsigned char)(-diff >> 8);
1584 req[16] = (unsigned char)(-diff);
1585 }
1586
1587 if (Recurrance != -1) {
1588 /* 8760 hours = 1 year */
1589 if (Note->Entries[Recurrance].Number >= 8760) {
1590 req[19] = 0xff;
1591 req[20] = 0xff;
1592 } else {
1593 req[19] = Note->Entries[Recurrance].Number / 256;
1594 req[20] = Note->Entries[Recurrance].Number % 256;
1595 }
1596 }
1597
1598 if (Text != -1) {
1599 switch (NoteType) {
1600 case GSM_CAL_CALL:
1601 req[26] = UnicodeLength(Note->Entries[Text].Text);
1602 break;
1603 default:
1604 req[length++] = UnicodeLength(Note->Entries[Text].Text);
1605 if (NoteType == GSM_CAL_MEMO || NoteType == GSM_CAL_MEETING) req[length++] = 0x00;
1606 }
1607 CopyUnicodeString(req+length,Note->Entries[Text].Text);
1608 length += UnicodeLength(Note->Entries[Text].Text)*2;
1609 }
1610
1611 req[length++] = 0x00;
1612 req[length++] = 0x00;
1613
1614 req[4] = req[17] = length-7;
1615
1616 smprintf(s, "Writing calendar note method 2\n");
1617 return GSM_WaitFor (s, req, length, 0x13, 4, ID_SetCalendarNote);
1618}
1619
1620/* method 1*/
1621GSM_Error N71_65_ReplyGetCalendarNotePos1(GSM_Protocol_Message msg, GSM_StateMachine *s,int *FirstCalendarPos)
1622{
1623 smprintf(s, "First calendar location: %i\n",msg.Buffer[4]*256+msg.Buffer[5]);
1624 *FirstCalendarPos = msg.Buffer[4]*256+msg.Buffer[5];
1625 return ERR_NONE;
1626}
1627
1628/* method 1*/
1629static GSM_Error N71_65_GetCalendarNotePos1(GSM_StateMachine *s)
1630{
1631 unsigned char req[] = {N6110_FRAME_HEADER, 0x31};
1632
1633 smprintf(s, "Getting first free calendar note location\n");
1634 return GSM_WaitFor (s, req, 4, 0x13, 4, ID_GetCalendarNotePos);
1635}
1636
1637/* method 1 */
1638GSM_Error N71_65_ReplyAddCalendar1(GSM_Protocol_Message msg, GSM_StateMachine *s)
1639{
1640#ifdef DEBUG
1641 smprintf(s, "Written calendar note type ");
1642 switch ((msg.Buffer[3]/2)-1) {
1643 case 0: smprintf(s, "Meeting");break;
1644 case 1: smprintf(s, "Call");break;
1645 case 2:smprintf(s, "Birthday");break;
1646 case 3:smprintf(s, "Reminder");break;
1647 }
1648 smprintf(s, " on location %d\n",msg.Buffer[4]*256+msg.Buffer[5]);
1649#endif
1650 return ERR_NONE;
1651}
1652
1653/* method 1 */
1654GSM_Error N71_65_AddCalendar1(GSM_StateMachine *s, GSM_CalendarEntry *Note, int *FirstCalendarPos)
1655{
1656 long seconds;
1657 GSM_Error error;
1658 GSM_DateTime DT;
1659 int Text, Time, Alarm, Phone, Recurrance, EndTime, Location, count=12;
1660 unsigned char req[5000] = {
1661 N6110_FRAME_HEADER,
1662 0x01, /* note type */
1663 0x00, 0x00, /* location ? */
1664 0x00, /* entry type */
1665 0x00,
1666 0x00, 0x00, /* Year */
1667 0x00, /* Month */
1668 0x00, /* Day */
1669 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1670
1671 error=N71_65_GetCalendarNotePos1(s);
1672 if (error!=ERR_NONE) return error;
1673 if (FirstCalendarPos != NULL) {
1674 req[4] = *FirstCalendarPos/256;
1675 req[5] = *FirstCalendarPos%256;
1676 }
1677
1678 switch(Note->Type) {
1679 case GSM_CAL_CALL : req[3]=0x03; req[6]=0x02; break;
1680 case GSM_CAL_BIRTHDAY: req[3]=0x05; req[6]=0x04; break;
1681 case GSM_CAL_MEMO : req[3]=0x07; req[6]=0x08; break;
1682 case GSM_CAL_MEETING :
1683 default : req[3]=0x01; req[6]=0x01; break;
1684 }
1685
1686 GSM_CalendarFindDefaultTextTimeAlarmPhoneRecurrance(Note, &Text, &Time, &Alarm, &Phone, &Recurrance, &EndTime, &Location);
1687
1688 if (Time == -1) return ERR_UNKNOWN;
1689 memcpy(&DT,&Note->Entries[Time].Date,sizeof(GSM_DateTime));
1690 req[8]= DT.Year / 256;
1691 req[9]= DT.Year % 256;
1692 req[10]= DT.Month;
1693 req[11]= DT.Day;
1694
1695 switch(Note->Type) {
1696 case GSM_CAL_BIRTHDAY:
1697 /* byte 12 and 13 */
1698 req[count++] = 0x00;
1699 req[count++] = 0x00;
1700
1701 /* Alarm - bytes 14 to 17 */
1702 req[count++] = 0x00;
1703 req[count++] = 0x00;
1704 req[count++] = 0xff;
1705 req[count++] = 0xff;
1706 if (Alarm != -1) {
1707#ifndef ENABLE_LGPL
1708 /* Comment from original source by Gabriele Zappi:
1709 * I try with Time.Year = Alarm.Year. If negative, I increase 1 year,
1710 * but only once ! This thing, because I may have Alarm period across
1711 * a year. (eg. Birthday on 2001-01-10 and Alarm on 2000-12-27)
1712 */
1713#endif
1714 DT.Year = Note->Entries[Alarm].Date.Year;
1715 seconds = Fill_Time_T(DT,8)-Fill_Time_T(Note->Entries[Alarm].Date,8);
1716 if (seconds<0L) {
1717 DT.Year++;
1718 seconds = Fill_Time_T(DT,8)-Fill_Time_T(Note->Entries[Alarm].Date,8);
1719 }
1720 if (seconds>=0L) {
1721 count -= 4;
1722 /* bytes 14 to 17 */
1723 req[count++] = (unsigned char)(seconds>>24);
1724 req[count++] = (unsigned char)((seconds>>16) & 0xff);
1725 req[count++] = (unsigned char)((seconds>>8) & 0xff);
1726 req[count++] = (unsigned char)(seconds & 0xff);
1727 }
1728 /* byte 18 */
1729 if (Note->Entries[Alarm].EntryType == CAL_SILENT_ALARM_DATETIME) req[count++] = 0x01; else req[count++] = 0x00;
1730 }
1731
1732 /* byte 19 and next */
1733 if (Text != -1) {
1734 req[count++] = UnicodeLength(Note->Entries[Text].Text);
1735 CopyUnicodeString(req+count,Note->Entries[Text].Text);
1736 count=count+2*UnicodeLength(Note->Entries[Text].Text);
1737 } else {
1738 req[count++] = 0x00;
1739 }
1740 break;
1741 case GSM_CAL_MEMO:
1742 /* byte 12 and 13 */
1743 if (Recurrance != -1) {
1744 /* 8760 hours = 1 year */
1745 if (Note->Entries[Recurrance].Number >= 8760) {
1746 req[count++] = 0xff;
1747 req[count++] = 0xff;
1748 } else {
1749 req[count++] = Note->Entries[Recurrance].Number / 256;
1750 req[count++] = Note->Entries[Recurrance].Number % 256;
1751 }
1752 } else {
1753 req[count++] = 0x00;
1754 req[count++] = 0x00;
1755 }
1756
1757 /* byte 14 and next */
1758 if (Text != -1) {
1759 req[count++] = UnicodeLength(Note->Entries[Text].Text);
1760 req[count++] = 0x00;
1761 CopyUnicodeString(req+count,Note->Entries[Text].Text);
1762 count=count+2*UnicodeLength(Note->Entries[Text].Text);
1763 } else {
1764 req[count++] = 0x00;
1765 req[count++] = 0x00;
1766 }
1767 break;
1768 case GSM_CAL_MEETING:
1769 case GSM_CAL_CALL:
1770 default:
1771 /* byte 12 and 13 */
1772 req[count++] = DT.Hour;
1773 req[count++] = DT.Minute;
1774
1775 /* Alarm - byte 14 and 15 */
1776 req[count++] = 0xff;
1777 req[count++] = 0xff;
1778 if (Alarm != -1) {
1779 seconds=Fill_Time_T(DT,8)-Fill_Time_T(Note->Entries[Alarm].Date,8);
1780 if (seconds>=0L) {
1781 count -= 2;
1782 req[count++] = ((unsigned char)(seconds/60L)>>8);
1783 req[count++] = (unsigned char)((seconds/60L)&0xff);
1784 }
1785 }
1786
1787 /* byte 16 and 17 */
1788 if (Recurrance != -1) {
1789 /* 8760 hours = 1 year */
1790 if (Note->Entries[Recurrance].Number >= 8760) {
1791 req[count++] = 0xff;
1792 req[count++] = 0xff;
1793 } else {
1794 req[count++] = Note->Entries[Recurrance].Number / 256;
1795 req[count++] = Note->Entries[Recurrance].Number % 256;
1796 }
1797 } else {
1798 req[count++] = 0x00;
1799 req[count++] = 0x00;
1800 }
1801
1802 /* byte 18 */
1803 if (Text != -1) {
1804 req[count++] = UnicodeLength(Note->Entries[Text].Text);
1805 } else {
1806 req[count++] = 0x00;
1807 }
1808 /* byte 19 */
1809 if (Note->Type == GSM_CAL_CALL && Phone != -1) {
1810 req[count++] = UnicodeLength(Note->Entries[Phone].Text);
1811 } else {
1812 req[count++] = 0x00;
1813 }
1814 if (Text != -1) {
1815 CopyUnicodeString(req+count,Note->Entries[Text].Text);
1816 count=count+2*UnicodeLength(Note->Entries[Text].Text);
1817 }
1818 if (Note->Type == GSM_CAL_CALL && Phone != -1) {
1819 CopyUnicodeString(req+count,Note->Entries[Phone].Text);
1820 count=count+2*UnicodeLength(Note->Entries[Phone].Text);
1821 }
1822 break;
1823 }
1824 req[count] = 0x00;
1825 smprintf(s, "Writing calendar note method 1\n");
1826 return GSM_WaitFor (s, req, count, 0x13, 4, ID_SetCalendarNote);
1827}
1828
1829GSM_Error N71_65_ReplyDelCalendar(GSM_Protocol_Message msg, GSM_StateMachine *s)
1830{
1831 smprintf(s, "Deleted calendar note on location %d\n",msg.Buffer[4]*256+msg.Buffer[5]);
1832 return ERR_NONE;
1833}
1834
1835GSM_Error N71_65_DelCalendar(GSM_StateMachine *s, GSM_CalendarEntry *Note)
1836{
1837 unsigned char req[] = {N6110_FRAME_HEADER, 0x0b,
1838 0x00, 0x00};/* location */
1839
1840 req[4] = Note->Location / 256;
1841 req[5] = Note->Location % 256;
1842
1843 smprintf(s, "Deleting calendar note\n");
1844 return GSM_WaitFor (s, req, 6, 0x13, 4, ID_DeleteCalendarNote);
1845}
1846
1847/* method 1 */
1848GSM_Error N71_65_ReplyGetCalendarInfo1(GSM_Protocol_Message msg, GSM_StateMachine *s, GSM_NOKIACalToDoLocations *LastCalendar)
1849{
1850 int i,j=0;
1851
1852 smprintf(s, "Info with calendar notes locations received method 1\n");
1853 while (LastCalendar->Location[j] != 0x00) j++;
1854 if (j >= GSM_MAXCALENDARTODONOTES) {
1855 smprintf(s, "Increase GSM_MAXCALENDARNOTES\n");
1856 return ERR_UNKNOWN;
1857 }
1858 if (j == 0) {
1859 LastCalendar->Number=msg.Buffer[4]*256+msg.Buffer[5];
1860 smprintf(s, "Number of Entries: %i\n",LastCalendar->Number);
1861 }
1862 smprintf(s, "Locations: ");
1863 i = 0;
1864 while (9+(i*2) <= msg.Length) {
1865 LastCalendar->Location[j++]=msg.Buffer[8+(i*2)]*256+msg.Buffer[9+(i*2)];
1866 smprintf(s, "%i ",LastCalendar->Location[j-1]);
1867 i++;
1868 }
1869 smprintf(s, "\nNumber of Entries in frame: %i\n",i);
1870 smprintf(s, "\n");
1871 LastCalendar->Location[j] = 0;
1872 if (i == 1 && msg.Buffer[8+(0*2)]*256+msg.Buffer[9+(0*2)] == 0) return ERR_EMPTY;
1873 if (i == 0) return ERR_EMPTY;
1874 return ERR_NONE;
1875}
1876
1877/* method 1 */
1878GSM_Error N71_65_GetCalendarInfo1(GSM_StateMachine *s, GSM_NOKIACalToDoLocations *LastCalendar)
1879{
1880 GSM_Errorerror;
1881 int i;
1882 unsigned char req[] = {N6110_FRAME_HEADER, 0x3a,
1883 0xFF, 0xFE};/* First location number */
1884
1885 LastCalendar->Location[0] = 0x00;
1886 LastCalendar->Number = 0;
1887
1888 smprintf(s, "Getting locations for calendar method 1\n");
1889 error = GSM_WaitFor (s, req, 6, 0x13, 4, ID_GetCalendarNotesInfo);
1890 if (error != ERR_NONE && error != ERR_EMPTY) return error;
1891
1892 while (1) {
1893 i=0;
1894 while (LastCalendar->Location[i] != 0x00) i++;
1895 if (i == LastCalendar->Number) break;
1896 if (i != LastCalendar->Number && error == ERR_EMPTY) {
1897 smprintf(s, "Phone doesn't support some notes with this method. Workaround\n");
1898 LastCalendar->Number = i;
1899 break;
1900 }
1901 smprintf(s, "i = %i %i\n",i,LastCalendar->Number);
1902 req[4] = LastCalendar->Location[i-1] / 256;
1903 req[5] = LastCalendar->Location[i-1] % 256;
1904 smprintf(s, "Getting locations for calendar\n");
1905 error = GSM_WaitFor (s, req, 6, 0x13, 4, ID_GetCalendarNotesInfo);
1906 if (error != ERR_NONE && error != ERR_EMPTY) return error;
1907 }
1908 return ERR_NONE;
1909}
1910
1911/* method 1 */
1912GSM_Error N71_65_ReplyGetNextCalendar1(GSM_Protocol_Message msg, GSM_StateMachine *s)
1913{
1914 int alarm,i;
1915 GSM_CalendarEntry*entry = s->Phone.Data.Cal;
1916
1917 smprintf(s, "Calendar note received method 1\n");
1918
1919 /* Later these values can change */
1920 if (msg.Buffer[6]!=0x04) { /* Here not birthday */
1921 entry->Entries[0].Date.Year = msg.Buffer[8]*256+msg.Buffer[9];
1922 }
1923 entry->Entries[0].Date.Month = msg.Buffer[10];
1924 entry->Entries[0].Date.Day= msg.Buffer[11];
1925 entry->Entries[0].Date.Hour= msg.Buffer[12];
1926 entry->Entries[0].Date.Minute= msg.Buffer[13];
1927 entry->Entries[0].Date.Second = 0;
1928 entry->Entries[0].EntryType = CAL_START_DATETIME;
1929 entry->EntriesNum++;
1930
1931 switch (msg.Buffer[6]) {
1932 case 0x01:
1933 smprintf(s, "Meeting\n");
1934 entry->Type = GSM_CAL_MEETING;
1935
1936 alarm=msg.Buffer[14]*256+msg.Buffer[15];
1937 if (alarm != 0xffff) {
1938 smprintf(s, " Difference : %i seconds\n", alarm);
1939 memcpy(&entry->Entries[1].Date,&entry->Entries[0].Date,sizeof(GSM_DateTime));
1940 GetTimeDifference(alarm, &entry->Entries[1].Date, false, 60);
1941 entry->Entries[1].EntryType = CAL_ALARM_DATETIME;
1942 entry->EntriesNum++;
1943 }
1944 N71_65_GetCalendarRecurrance(s, msg.Buffer + 16, entry);
1945
1946 memcpy(entry->Entries[entry->EntriesNum].Text, msg.Buffer+20, msg.Buffer[18]*2);
1947 entry->Entries[entry->EntriesNum].Text[msg.Buffer[18]*2] = 0;
1948 entry->Entries[entry->EntriesNum].Text[msg.Buffer[18]*2+1] = 0;
1949 entry->Entries[entry->EntriesNum].EntryType = CAL_TEXT;
1950 smprintf(s, "Text : \"%s\"\n", DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
1951 entry->EntriesNum++;
1952 return ERR_NONE;
1953 case 0x02:
1954 smprintf(s, "Call\n");
1955 entry->Type = GSM_CAL_CALL;
1956
1957 alarm=msg.Buffer[14]*256+msg.Buffer[15];
1958 if (alarm != 0xffff) {
1959 smprintf(s, " Difference : %i seconds\n", alarm);
1960 memcpy(&entry->Entries[1].Date,&entry->Entries[0].Date,sizeof(GSM_DateTime));
1961 GetTimeDifference(alarm, &entry->Entries[1].Date, false, 60);
1962 entry->Entries[1].EntryType = CAL_ALARM_DATETIME;
1963 entry->EntriesNum++;
1964 }
1965 N71_65_GetCalendarRecurrance(s, msg.Buffer + 16, entry);
1966
1967 i = msg.Buffer[18] * 2;
1968 if (i!=0) {
1969 memcpy(entry->Entries[entry->EntriesNum].Text, msg.Buffer+20, i);
1970 entry->Entries[entry->EntriesNum].Text[i] = 0;
1971 entry->Entries[entry->EntriesNum].Text[i+1] = 0;
1972 entry->Entries[entry->EntriesNum].EntryType= CAL_TEXT;
1973 smprintf(s, "Text : \"%s\"\n", DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
1974 entry->EntriesNum++;
1975 }
1976
1977 memcpy(entry->Entries[entry->EntriesNum].Text, msg.Buffer+20+i, msg.Buffer[19]*2);
1978 entry->Entries[entry->EntriesNum].Text[msg.Buffer[19]*2] = 0;
1979 entry->Entries[entry->EntriesNum].Text[msg.Buffer[19]*2+1] = 0;
1980 entry->Entries[entry->EntriesNum].EntryType = CAL_PHONE;
1981 smprintf(s, "Phone : \"%s\"\n", DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
1982 entry->EntriesNum++;
1983 return ERR_NONE;
1984 case 0x04:
1985 smprintf(s, "Birthday\n");
1986 entry->Type = GSM_CAL_BIRTHDAY;
1987
1988 entry->Entries[0].Date.Hour= 23;
1989 entry->Entries[0].Date.Minute= 59;
1990 entry->Entries[0].Date.Second= 58;
1991
1992 alarm = ((unsigned int)msg.Buffer[14]) << 24;
1993 alarm += ((unsigned int)msg.Buffer[15]) << 16;
1994 alarm += ((unsigned int)msg.Buffer[16]) << 8;
1995 alarm += msg.Buffer[17];
1996 if (alarm != 0xffff) {
1997 smprintf(s, " Difference : %i seconds\n", alarm);
1998 memcpy(&entry->Entries[1].Date,&entry->Entries[0].Date,sizeof(GSM_DateTime));
1999 GetTimeDifference(alarm, &entry->Entries[1].Date, false, 1);
2000 entry->Entries[1].EntryType = CAL_ALARM_DATETIME;
2001 if (msg.Buffer[20]!=0x00) {
2002 entry->Entries[1].EntryType = CAL_SILENT_ALARM_DATETIME;
2003 smprintf(s, "Alarm type : Silent\n");
2004 }
2005 entry->EntriesNum++;
2006 }
2007
2008 entry->Entries[0].Date.Year = msg.Buffer[18]*256 + msg.Buffer[19];
2009 if (entry->Entries[0].Date.Year == 65535) entry->Entries[0].Date.Year = 0;
2010 smprintf(s, "Age : %i\n",entry->Entries[0].Date.Year);
2011
2012 memcpy(entry->Entries[entry->EntriesNum].Text, msg.Buffer+22, msg.Buffer[21]*2);
2013 entry->Entries[entry->EntriesNum].Text[msg.Buffer[21]*2] = 0;
2014 entry->Entries[entry->EntriesNum].Text[msg.Buffer[21]*2+1] = 0;
2015 entry->Entries[entry->EntriesNum].EntryType = CAL_TEXT;
2016 smprintf(s, "Text : \"%s\"\n", DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
2017 entry->EntriesNum++;
2018 return ERR_NONE;
2019 case 0x08:
2020 smprintf(s, "Memo\n");
2021 entry->Type = GSM_CAL_MEMO;
2022
2023 entry->Entries[0].Date.Hour= 0;
2024 entry->Entries[0].Date.Minute= 0;
2025
2026 N71_65_GetCalendarRecurrance(s, msg.Buffer + 12, entry);
2027
2028 memcpy(entry->Entries[entry->EntriesNum].Text, msg.Buffer+16, msg.Buffer[14]*2);
2029 entry->Entries[entry->EntriesNum].Text[msg.Buffer[14]*2] = 0;
2030 entry->Entries[entry->EntriesNum].Text[msg.Buffer[14]*2+1] = 0;
2031 entry->Entries[entry->EntriesNum].EntryType = CAL_TEXT;
2032 smprintf(s, "Text : \"%s\"\n", DecodeUnicodeString(entry->Entries[entry->EntriesNum].Text));
2033 entry->EntriesNum++;
2034 return ERR_NONE;
2035 default:
2036 smprintf(s, "ERROR: unknown %i\n",msg.Buffer[6]);
2037 return ERR_UNKNOWNRESPONSE;
2038 }
2039}
2040
2041/* method 1 */
2042GSM_Error N71_65_GetNextCalendar1(GSM_StateMachine *s, GSM_CalendarEntry *Note, bool start, GSM_NOKIACalToDoLocations *LastCalendar, int *LastCalendarYear, int *LastCalendarPos)
2043{
2044 GSM_Error error;
2045 GSM_DateTime date_time;
2046 unsigned char req[] = {N6110_FRAME_HEADER, 0x19,
2047 0x00, 0x00}; /* Location */
2048
2049 if (start) {
2050 error=N71_65_GetCalendarInfo1(s, LastCalendar);
2051 if (error!=ERR_NONE) return error;
2052 if (LastCalendar->Number == 0) return ERR_EMPTY;
2053
2054 /* We have to get current year. It's NOT written in frame for
2055 * Birthday
2056 */
2057 error=s->Phone.Functions->GetDateTime(s,&date_time);
2058 switch (error) {
2059 case ERR_EMPTY:
2060 case ERR_NOTIMPLEMENTED:
2061 GSM_GetCurrentDateTime(&date_time);
2062 break;
2063 case ERR_NONE:
2064 break;
2065 default:
2066 return error;
2067 }
2068 *LastCalendarYear = date_time.Year;
2069 *LastCalendarPos = 0;
2070 } else {
2071 (*LastCalendarPos)++;
2072 }
2073
2074 if (*LastCalendarPos >= LastCalendar->Number) return ERR_EMPTY;
2075
2076 req[4] = LastCalendar->Location[*LastCalendarPos] / 256;
2077 req[5] = LastCalendar->Location[*LastCalendarPos] % 256;
2078
2079 Note->EntriesNum = 0;
2080 Note->Entries[0].Date.Year = *LastCalendarYear;
2081 Note->Location = LastCalendar->Location[*LastCalendarPos];
2082
2083 s->Phone.Data.Cal=Note;
2084 smprintf(s, "Getting calendar note method 1\n");
2085 return GSM_WaitFor (s, req, 6, 0x13, 4, ID_GetCalendarNote);
2086}
2087
2088GSM_Error N71_65_EnableFunctions(GSM_StateMachine *s,char *buff,int len)
2089{
2090 unsigned char buffer[50] = {N6110_FRAME_HEADER, 0x10,
2091 0x07};/* Length */
2092
2093 buffer[4] = len;
2094 memcpy(buffer+5,buff,len);
2095
2096 /* Enables various things like incoming SMS, call info, etc. */
2097 return s->Protocol.Functions->WriteMessage(s, buffer, 5+len, 0x10);
2098}
2099
2100GSM_Error N71_65_ReplySendDTMF(GSM_Protocol_Message msg, GSM_StateMachine *s)
2101{
2102 switch (msg.Buffer[3]) {
2103 case 0x51:
2104 smprintf(s, "DTMF sent OK\n");
2105 return ERR_NONE;
2106 case 0x59:
2107 case 0x5E:
2108 smprintf(s, "meaning unknown - during sending DTMF\n");
2109 return ERR_NONE;
2110 }
2111 return ERR_UNKNOWNRESPONSE;
2112}
2113
2114GSM_CalendarNoteType N71_65_FindCalendarType(GSM_CalendarNoteType Type, OnePhoneModel *model)
2115{
2116 switch (Type) {
2117 case GSM_CAL_CALL:
2118 return GSM_CAL_CALL;
2119 case GSM_CAL_BIRTHDAY:
2120 return GSM_CAL_BIRTHDAY;
2121 case GSM_CAL_MEETING:
2122 if (IsPhoneFeatureAvailable(model, F_CAL35)) {
2123 return GSM_CAL_REMINDER;
2124 } else return GSM_CAL_MEETING;
2125 case GSM_CAL_MEMO:
2126 if (IsPhoneFeatureAvailable(model, F_CAL35)) {
2127 return GSM_CAL_REMINDER;
2128 } else return GSM_CAL_MEMO;
2129 case GSM_CAL_REMINDER:
2130 if (IsPhoneFeatureAvailable(model, F_CAL62) ||
2131 IsPhoneFeatureAvailable(model, F_CAL65)) {
2132 return GSM_CAL_CALL;
2133 } else return GSM_CAL_REMINDER;
2134 default:
2135 return GSM_CAL_CALL;
2136 }
2137}
2138
2139#endif
2140
2141/* How should editor hadle tabs in this file? Add editor commands here.
2142 * vim: noexpandtab sw=8 ts=8 sts=8:
2143 */