:pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Dec 4 22:45 CET 2001
[gnokii.git] / common / mbus-2110.c
1 /* -*- linux-c -*-
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 2000 Pavel Machek <pavel@ucw.cz>
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12   
13   $Log$
14   Revision 1.1.1.1  2001/11/25 21:59:04  short
15   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
16
17   Revision 1.39  2001/09/14 12:15:28  pkot
18   Cleanups from 0.3.3 (part1)
19
20   Revision 1.38  2001/07/03 00:03:36  pkot
21   Small fixes to let gnokii compile and work under solaris (thanks to Artur Kubiak)
22
23   Revision 1.37  2001/03/05 08:02:16  machek
24   Added few static-s.
25
26   Revision 1.36  2001/02/27 22:16:25  machek
27   Kill unneccessary variable and annoying warning.
28
29   Revision 1.35  2001/02/25 19:15:33  machek
30   Fixed udelay() to work w.r.t. signals.
31
32   Revision 1.34  2001/02/21 19:56:58  chris
33   More fiddling with the directory layout
34
35   Revision 1.33  2001/02/20 21:30:28  machek
36   We really have 150 addressbook places, better
37   getrflevel/getbatterylevel, wait one second on collision.
38
39   Revision 1.32  2001/02/17 22:40:49  chris
40   ATA support
41
42   Revision 1.31  2001/02/17 14:39:19  machek
43   Make writing/reading phonebook less verbose (as it should be).
44
45   Revision 1.30  2001/02/01 15:17:30  pkot
46   Fixed --identify and added Manfred's manufacturer patch
47
48   Revision 1.29  2001/01/31 11:46:50  machek
49   Comment out SendSMSMessage to kill warning.
50
51   Revision 1.28  2001/01/29 17:14:42  chris
52   dprintf now in misc.h (and fiddling with 7110 code)
53
54   Revision 1.27  2001/01/23 18:58:51  machek
55   Implement collision protocol slightly better. It is still not
56   completely okay -- we should check if echo is exactly same characters
57   as we sent.
58
59   Revision 1.26  2001/01/23 15:32:38  chris
60   Pavel's 'break' and 'static' corrections.
61   Work on logos for 7110.
62
63   Revision 1.25  2001/01/12 14:09:12  pkot
64   More cleanups. This time mainly in the code.
65
66   Revision 1.24  2001/01/10 16:32:17  pkot
67   Documentation updates.
68   FreeBSD fix for 3810 code.
69   Added possibility for deleting SMS just after reading it in gnokii.
70   2110 code updates.
71   Many cleanups.
72
73   Revision 1.23  2001/01/02 09:09:08  pkot
74   Misc fixes and updates.
75
76   Revision 1.22  2000/12/20 09:11:19  pkot
77   Fixes to mbus-2110.c to let it compile (by Pavel Machek)
78
79   Revision 1.21  2000/12/19 16:32:28  pkot
80   Lots of updates in common/mbus-2110.c. (thanks to Pavel Machek)
81
82 */
83
84 #ifndef WIN32
85
86 #include "misc.h"
87
88 #include <termios.h>
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <unistd.h>
92 #include <fcntl.h>
93 #include <ctype.h>
94 #include <signal.h>
95 #include <sys/types.h>
96 #include <sys/time.h>
97 #include <sys/ioctl.h>
98 #include <string.h>
99 #include <pthread.h>
100 #include <errno.h>
101
102 #if __unices__
103 #  include <sys/file.h>
104 #endif
105
106 #include "gsm-common.h"
107 #include "mbus-2110.h"
108 #include "phones/nokia.h"
109
110 #define MYID 0x78
111
112 #undef VELO
113 #ifdef VELO
114 #define usleep(x) do { usleep(x); SigHandler(0); } while (0)
115 #endif
116
117 #define msleep(x) do { usleep(x*1000); } while (0)
118
119 #define THREAD
120
121 /* Global variables used by code in gsm-api.c to expose the
122    functions supported by this model of phone.  */
123 bool MB21_LinkOK;
124 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
125 static char *Revision = NULL,
126         *RevisionDate = NULL,
127         *Model = NULL,
128         VersionInfo[64];
129
130 GSM_Information MB21_Information = {
131         "2110|2140|6080",               /* Models */
132         100,                            /* Max RF Level */
133         0,                              /* Min RF Level */
134         GRF_Percentage,                 /* RF level units */
135         100,                                    /* Max Battery Level */
136         0,                              /* Min Battery Level */
137         GBU_Percentage,                 /* Battery level units */
138         GDT_None,                       /* No date/time support */
139         GDT_None,                       /* No alarm support */
140         0,                              /* Max alarms = 0 */
141         0, 0,                           /* Startup logo size */
142         0, 0,                           /* Op logo size */
143         0, 0                            /* Caller logo size */
144 };
145
146 /* Local variables */
147 #ifdef THREAD
148 static pthread_t Thread;
149 #endif
150 static volatile bool RequestTerminate;
151 static int PortFD;
152 static struct termios old_termios; /* old termios */
153 static u8 TXPacketNumber = 0x01;
154 #define MAX_MODEL_LENGTH 16
155 static bool ModelValid     = false;
156 static volatile unsigned char PacketData[256];
157 static volatile bool
158         ACKOK    = false,
159         PacketOK = false,
160         EchoOK   = false;
161
162 static volatile int SMSpos = 0;
163 static volatile unsigned char SMSData[256];
164
165 static long long LastChar = 0;
166
167 static long long
168 GetTime(void)
169 {
170         struct timeval tv;
171
172         gettimeofday(&tv, NULL);
173         return (long long) tv.tv_sec * 1000000 + tv.tv_usec;
174 }
175
176 static void
177 Wait(long long from, int msec)
178 {
179         while (GetTime() < from + ((long long) msec)*1000)
180                 usleep(5000);
181 }
182
183 #define usleep(x) do { Wait(GetTime(), x/1000); } while (0)
184
185 /* Checksum calculation */
186 static u8
187 GetChecksum( u8 * packet, int len )
188 {
189         u8 checksum = 0;
190         unsigned int i;
191
192         for( i = 0; i < len; i++ ) checksum ^= packet[i]; /* calculate checksum */
193         return checksum;
194 }
195
196 static GSM_Error
197 SendFrame( u8 *buffer, u8 command, u8 length )
198 {
199         u8  pkt[256];
200         int current = 0;
201
202         pkt[current++] = 0x00;               /* Construct the header.      */
203         pkt[current++] = MYID;
204         pkt[current++] = length;             /* Set data size              */
205         pkt[current++] = command;
206         memcpy( pkt + current, buffer, length ); /* Copy in data.          */
207         current += length;
208         pkt[current++] = TXPacketNumber;         /* Set packet number      */
209         pkt[current++] = GetChecksum( pkt, current); /* Calculate and set checksum */
210 #ifdef DEBUG
211         {
212                 int i;
213                 u8  b;
214                 fprintf( stderr, _("PC   : ") );
215                 for( i = 0; i < current; i++ ) {
216                         b = pkt[i];
217                         fprintf( stderr, "[%02X %c]", b, b > 0x20 ? b : '.' );
218                 }
219                 fprintf( stderr, "\n" );
220         }
221 #endif /* DEBUG */
222         /* Send it out... */
223         EchoOK = false;
224         dprintf("(");
225         Wait(LastChar, 3);
226         /* I should put my messages at least 2msec apart... */
227         dprintf(")");
228         if (write(PortFD, pkt, current) != current) /* BUGGY, we should handle -EINTR and short writes! */ {
229                 perror( _("Write error!\n") );
230                 return (GE_INTERNALERROR);
231         }
232         /* wait for echo */
233         while( !EchoOK && current-- ) 
234                 usleep(1300);
235         if( !EchoOK ) {
236                 fprintf(stderr, "no echo?!");
237                 usleep(1000000);
238                 return (GE_TIMEOUT);
239         }
240         return (GE_NONE);
241 }
242
243 static GSM_Error
244 SendCommand( u8 *buffer, u8 command, u8 length )
245 {
246         int time, retries = 10;
247
248         ACKOK = false;
249         while (retries--) {
250                 SendFrame(buffer, command, length);
251                 time = 20;
252                 while (time--) {
253                         if (ACKOK)
254                                 return GE_NONE;
255                         msleep(10);
256                 }
257                 fprintf(stderr, "[resend]");
258         }
259         fprintf(stderr, "Command not okay after 10 retries!\n");
260         return GE_NONE;
261 }
262
263 /* Applications should call Terminate to close the serial port. */
264 static void
265 Terminate(void)
266 {
267         /* Request termination of thread */
268         RequestTerminate = true;
269 #ifdef THREAD
270         /* Now wait for thread to terminate. */
271         pthread_join(Thread, NULL);
272 #endif
273         /* Close serial port. */
274         if( PortFD >= 0 ) {
275                 tcsetattr(PortFD, TCSANOW, &old_termios);
276                 close( PortFD );
277         }
278 }
279
280 static GSM_Error
281 SMS(GSM_SMSMessage *message, int command)
282 {
283         u8 pkt[] = { 0x10, 0x02, 0, 0 };
284
285         SMSpos = 0;
286         memset((void *) &SMSData[0], 0, 160);
287         PacketOK = false;
288         pkt[1] = command;
289         pkt[2] = 1; /* == LM_SMS_MEM_TYPE_DEFAULT or LM_SMS_MEM_TYPE_SIM or LM_SMS_MEM_TYPE_ME */
290         pkt[3] = message->Location;
291         message->MessageNumber = message->Location;
292
293         SendCommand(pkt, 0x38 /* LN_SMS_COMMAND */, sizeof(pkt));
294         while(!PacketOK) {
295                 dprintf(".");
296                 usleep(100000);
297                 if(0) {
298                         fprintf(stderr, _("Impossible timeout?\n"));
299                         return GE_BUSY;
300                 }
301         }
302         if (PacketData[3] != 0x37 /* LN_SMS_EVENT */) {
303                 fprintf(stderr, _("Something is very wrong with GetValue\n"));
304                 return GE_BUSY; /* FIXME */
305         }
306         return (GE_NONE);
307 }
308
309 static GSM_Error
310 GetSMSMessage(GSM_SMSMessage *m)
311 {
312         int i, len;
313         if (SMS(m, 2) != GE_NONE)
314                 return GE_BUSY; /* FIXME */
315         dprintf("Have message?\n");
316         if (m->Location > 10)
317                 return GE_INVALIDSMSLOCATION;
318
319         if (SMSData[0] != 0x0b) {
320                 dprintf("No sms there? (%x/%d)\n", SMSData[0], SMSpos);
321                 return GE_EMPTYSMSLOCATION;
322         }
323         dprintf("Status: " );
324         switch (SMSData[3]) {
325         case 7: m->Type = GST_MO; m->Status = GSS_NOTSENTREAD; dprintf("not sent\n"); break;
326         case 5: m->Type = GST_MO; m->Status = GSS_SENTREAD; dprintf("sent\n"); break;
327         case 3: m->Type = GST_MT; m->Status = GSS_NOTSENTREAD; dprintf("not read\n"); break;
328         case 1: m->Type = GST_MT; m->Status = GSS_SENTREAD; dprintf("read\n"); break;
329         }
330
331         /* Date is at SMSData[7]; this code is copied from fbus-6110.c*/
332         {
333                 int offset = -32 + 7;
334                 m->Time.Year=10*(SMSData[32+offset]&0x0f)+(SMSData[32+offset]>>4);
335                 m->Time.Month=10*(SMSData[33+offset]&0x0f)+(SMSData[33+offset]>>4);
336                 m->Time.Day=10*(SMSData[34+offset]&0x0f)+(SMSData[34+offset]>>4);
337                 m->Time.Hour=10*(SMSData[35+offset]&0x0f)+(SMSData[35+offset]>>4);
338                 m->Time.Minute=10*(SMSData[36+offset]&0x0f)+(SMSData[36+offset]>>4);
339                 m->Time.Second=10*(SMSData[37+offset]&0x0f)+(SMSData[37+offset]>>4);
340                 m->Time.Timezone=(10*(SMSData[38+offset]&0x07)+(SMSData[38+offset]>>4))/4;
341         }
342
343         len = SMSData[14];
344         dprintf("%d bytes: ", len );
345         for (i = 0; i<len; i++)
346                 dprintf("%c", SMSData[15+i]);
347         dprintf("\n");
348
349         if (len>160)
350                 fprintf(stderr, "Magic not allowed\n");
351         memset(m->MessageText, 0, 161);
352         strncpy(m->MessageText, (void *) &SMSData[15], len);
353         m->Length = len;
354         dprintf("Text is %s\n", m->MessageText);
355
356         /* Originator address is at 15+i,
357            followed by message center addres (?) */
358         {
359                 char *s = (char *) &SMSData[15+i];      /* We discard volatile. Make compiler quiet. */
360                 strcpy(m->Sender, s);
361                 s+=strlen(s)+1;
362                 strcpy(m->MessageCenter.Number, s);
363                 dprintf("Sender = %s, MessageCenter = %s\n", m->Sender, m->MessageCenter.Name);
364         }
365
366         m->MessageCenter.No = 0;
367         strcpy(m->MessageCenter.Name, "(unknown)");
368         m->UDHType = GSM_NoUDH;
369         usleep(300000);         /* If phone lost our ack, it might retransmit data */
370         return (GE_NONE);
371 }
372
373 #if 0
374 static GSM_Error
375 SendSMSMessage(GSM_SMSMessage *m)
376 {
377         if (m->Location > 10)
378                 return GE_INVALIDSMSLOCATION;
379
380         if (SMSData[0] != 0x0b) {
381                 dprintf("No sms there? (%x/%d)\n", SMSData[0], SMSpos);
382                 return GE_EMPTYSMSLOCATION;
383         }
384         dprintf("Status: " );
385         switch (SMSData[3]) {
386         case 7: m->Type = GST_MO; m->Status = GSS_NOTSENTREAD; dprintf("not sent\n"); break;
387         case 5: m->Type = GST_MO; m->Status = GSS_SENTREAD; dprintf("sent\n"); break;
388         case 3: m->Type = GST_MT; m->Status = GSS_NOTSENTREAD; dprintf("not read\n"); break;
389         case 1: m->Type = GST_MT; m->Status = GSS_SENTREAD; dprintf("read\n"); break;
390         }
391         return (GE_NONE);
392 }
393 #endif
394
395 static GSM_Error        DeleteSMSMessage(GSM_SMSMessage *message)
396 {
397         dprintf("deleting...");
398         return SMS(message, 3);
399 }
400
401 /* GetRFLevel */
402
403 static int
404 GetValue(u8 index, u8 type)
405 {
406         u8  pkt[] = {0x84, 2, 0};       /* Sending 4 at pkt[0] makes phone crash */
407         int val;
408         pkt[0] = index;
409         pkt[1] = type;
410
411         PacketOK = false;
412
413         dprintf("\nRequesting value(%d)", index);
414         SendCommand(pkt, 0xe5, 3);
415
416         while(!PacketOK) {
417                 usleep(1000000);
418         }
419         if ((PacketData[3] != 0xe6) ||
420             (PacketData[4] != index) || 
421             (PacketData[5] != type))
422                 fprintf(stderr, "Something is very wrong with GetValue\n");
423         val = PacketData[7];
424         dprintf( "Value = %d\n", val );
425         return (val);
426 }
427
428 static GSM_Error
429 GetRFLevel(GSM_RFUnits *units, float *level)
430 {
431         int val = GetValue(0x84, 2);
432         float res;
433         if (*units == GRF_Arbitrary) {
434                 res = (100* (float) val) / 60.0;        /* This should be / 99.0 for some models other than nokia-2110 */
435                 *level = 0;
436                 if (res > 10)
437                         *level = 1;
438                 if (res > 30)
439                         *level = 2;
440                 if (res > 50)
441                         *level = 3;
442                 if (res > 70)
443                         *level = 4;
444         } else {
445                 *level = (100* (float) val) / 60.0;     /* This should be / 99.0 for some models other than nokia-2110 */
446                 *units = GRF_Percentage;
447         }
448         return (GE_NONE);
449 }
450
451 static GSM_Error
452 GetBatteryLevel(GSM_BatteryUnits *units, float *level)
453 {
454         int val = GetValue(0x85, 2);
455         *level = 0;
456         if (val >= 5)
457                 *level = 1;
458         if (val >= 10)
459                 *level = 2;
460         if (val >= 90)
461                 *level = 3;
462         if (*units == GBU_Arbitrary) {
463         } else {
464 /*              *level = (100 * (float) val) / 90.0;*/  /* 5..first bar, 10..second bar, 90..third bar */
465                 *level = *level * 33;
466                 *units = GBU_Percentage;
467         }
468
469         return (GE_NONE);
470 }
471
472 static GSM_Error GetVersionInfo()
473 {
474         char *s = VersionInfo;
475         if (GetValue(0x11, 0x03) == -1)
476                 return GE_TIMEOUT;
477
478         strncpy( s, (void *) &PacketData[6], sizeof(VersionInfo) );
479
480         for( Revision     = s; *s != 0x0A; s++ ) if( !*s ) return (GE_NONE);
481         *s++ = 0;
482         for( RevisionDate = s; *s != 0x0A; s++ ) if( !*s ) return (GE_NONE);
483         *s++ = 0;
484         for( Model        = s; *s != 0x0A; s++ ) if( !*s ) return (GE_NONE);
485         *s++ = 0;
486         dprintf("Revision %s, Date %s, Model %s\n", Revision, RevisionDate, Model );
487         ModelValid = true;
488         return (GE_NONE);
489 }
490
491 static GSM_Error
492 GetRevision(char *revision)
493 {
494         GSM_Error err = GE_NONE;
495
496         if(!Revision) err = GetVersionInfo();
497         if(err == GE_NONE) strncpy(revision, Revision, 64);
498
499         return err;
500 }
501
502 static GSM_Error
503 GetModel2110(char *model)
504 {
505         GSM_Error err = GE_NONE;
506
507         if(!Model) err = GetVersionInfo();
508         if(err == GE_NONE) strncpy(model, Model, 64);
509
510         return err;
511 }
512
513 /* Our "Not implemented" functions */
514 static GSM_Error
515 GetMemoryStatus(GSM_MemoryStatus *Status)
516 {
517         switch(Status->MemoryType) {
518         case GMT_ME:
519                 Status->Used = 0;
520                 Status->Free = 150;
521                 break;
522         case GMT_LD:
523                 Status->Used = 5;
524                 Status->Free = 0;
525                 break;
526         case GMT_ON:
527                 Status->Used = 1;
528                 Status->Free = 0;
529                 break;
530         case GMT_SM:
531                 Status->Used = 0;
532                 Status->Free = 150;
533                 break;
534         default:
535                 return (GE_NOTIMPLEMENTED);
536         }
537         return (GE_NONE);
538 }
539
540 static bool
541 SendRLPFrame(RLP_F96Frame *frame, bool out_dtx)
542 {
543         return (false);
544 }
545
546 static void
547 Display(u8 b, int shift, char *s)
548 {
549         b >>= shift;
550         b &= 0x03;
551         switch (b) {
552         case 0: return;
553         case 1: case 2: printf( "!!!" );
554         case 3: printf("%s ", s);
555         }
556 }
557 static int
558 HandlePacket(void)
559 {
560         switch(PacketData[3]) {
561         case 0x12: 
562                 printf("Display text at (%d,%d: %s)\n", PacketData[6], PacketData[7], &PacketData[8]);
563                 return 1;
564         case 0x2f:
565                 printf("Display indicators: "); /* Valid for 2110 */
566                 Display(PacketData[4], 0, "d");
567                 Display(PacketData[4], 2, "b");
568                 Display(PacketData[4], 4, "a");
569                 Display(PacketData[4], 6, "lights");
570
571                 Display(PacketData[5], 0, "service");
572                 Display(PacketData[5], 2, "scroll_up");
573                 Display(PacketData[5], 4, "scroll_down");
574                 Display(PacketData[5], 6, "ABC");
575
576                 Display(PacketData[6], 0, "2.>");
577                 Display(PacketData[6], 2, "1.>");
578                 Display(PacketData[6], 4, "roam");
579                 Display(PacketData[6], 6, "handset");
580
581                 Display(PacketData[7], 0, "vmail");
582                 Display(PacketData[7], 2, "envelope");
583                 Display(PacketData[7], 4, "battbar");
584                 Display(PacketData[7], 6, "3.>");
585
586                 Display(PacketData[8], 0, "?1");
587                 Display(PacketData[8], 2, "?2");
588                 Display(PacketData[8], 4, "fieldbar");
589                 Display(PacketData[8], 6, "ring");
590                 printf("\n");
591                 return 1;
592
593         case 0x37:
594                 /* copy bytes 5+ to smsbuf */
595                 dprintf("SMSdata:");
596                 {
597                         int i;
598                         for (i=5; i<PacketData[2]+4; i++) {
599                                 SMSData[SMSpos++] = PacketData[i];
600                                 dprintf("%c", PacketData[i]);
601                         }
602                         fflush(stdout);
603                 }
604                 return ((PacketData[4] & 0xf) != 0);
605                 /* Make all but last fragment "secret" */
606
607         default: return 0;
608         }       
609 }
610
611 /* Handler called when characters received from serial port. 
612  * and process them. */
613 static void
614 SigHandler(int status)
615 {
616         unsigned char        buffer[256], ack[5], b;
617         int                  i, res;
618         static unsigned int  Index = 0, Length = 5;
619         static unsigned char pkt[256];
620 #ifdef DEBUG
621         int                  j;
622 #endif
623
624         res = read(PortFD, buffer, 256);
625         if( res < 0 ) return;
626         for(i = 0; i < res ; i++) {
627                 b = buffer[i];
628 //       fprintf(stderr, "(%x)", b, Index);
629                 if (!Index && b != MYID && b != 0xf8 && b != 0x00) /* MYID is code of computer */ {
630                         /* something strange goes from phone. Just ignore it */
631 #ifdef DEBUG
632                         fprintf( stdout, "Get [%02X %c]\n", b, b >= 0x20 ? b : '.' );
633 #endif /* DEBUG */
634                         continue;
635                 } else {
636                         pkt[Index++] = b;
637                         if(Index == 3) {
638                                 Length = b + 6;
639                                 if (b == 0x7f) Length = 5;
640                                 if (b == 0x7e) Length = 8;
641                         }
642                         if(Index >= Length) {
643                                 if((pkt[0] == MYID || pkt[0]==0xf8) && pkt[1] == 0x00) /* packet from phone */ {
644 #ifdef DEBUG
645                                         fprintf( stdout, _("Phone: ") );
646                                         for( j = 0; j < Length; j++ ) {
647                                                 b = pkt[j];
648                                                 fprintf( stdout, "[%02X %c]", b, b >= 0x20 ? b : '.' );
649                                         }
650                                         fprintf( stdout, "\n" );
651 #endif /* DEBUG */
652                                         /* ensure that we received valid packet */
653                                         if(pkt[Length - 1] != GetChecksum(pkt, Length-1)) {
654                                                 fprintf( stderr, "***bad checksum***");
655                                         } else {
656                                                 if((pkt[2] == 0x7F) || (pkt[2] == 0x7E)) /* acknowledge by phone */ {
657                                                         if (pkt[2] == 0x7F) {
658                                                                 fprintf( stderr, "[ack]" );
659                                                                 /* Set ACKOK flag */
660                                                                 ACKOK    = true;
661                                                                 /* Increase TX packet number */
662                                                         } else {
663                                                                 fprintf( stderr, "[registration ack]" );
664                                                                 MB21_LinkOK = true;
665                                                         }
666                                                         TXPacketNumber++;
667                                                 } else {
668                                                         /* Copy packet data  */
669                                                         fprintf( stderr, "[data]" );
670                                                         memcpy((void *) PacketData,pkt,Length);
671                                                         /* send acknowledge packet to phone */
672                                                         usleep(100000);
673                                                         ack[0] = 0x00;                     /* Construct the header.   */
674                                                         ack[1] = pkt[0];                   /* Send back id            */
675                                                         ack[2] = 0x7F;                     /* Set special size value  */
676                                                         ack[3] = pkt[Length - 2];          /* Send back packet number */
677                                                         ack[4] = GetChecksum( ack, 4); /* Set checksum            */
678 #ifdef DEBUG
679                                                         fprintf( stdout, _("PC   : ") );
680                                                         for( j = 0; j < 5; j++ ) {
681                                                                 b = ack[j];
682                                                                 fprintf( stdout, "[%02X %c]", b, b >= 0x20 ? b : '.' );
683                                                         }
684                                                         fprintf( stdout, "\n" );
685 #endif /* DEBUG */
686                                                         if( write( PortFD, ack, 5 ) != 5 )
687                                                                 perror( _("Write error!\n") );
688                                                         /* Set validity flag */
689                                                         if (!HandlePacket())
690                                                                 PacketOK = true;
691                                                 }
692                                         }
693                                 } else {
694                                         fprintf(stderr, "[echo]\n");
695                                         EchoOK = true;
696                                 }
697                                 /* Look for new packet */
698                                 Index  = 0;
699                                 Length = 5;
700                         }
701                 }
702         }
703 }
704
705 /* Called by initialisation code to open comm port in asynchronous mode. */
706 bool OpenSerial(void)
707 {
708         struct termios    new_termios;
709         struct sigaction  sig_io;
710         unsigned int      flags;
711
712 #ifdef DEBUG
713         fprintf(stdout, _("Setting MBUS communication with 2110...\n"));
714 #endif /* DEBUG */
715  
716         PortFD = open(PortDevice, O_RDWR | O_NOCTTY | O_NONBLOCK);
717         if ( PortFD < 0 ) { 
718                 fprintf(stderr, "Failed to open %s ...\n", PortDevice);
719                 return (false);
720         }
721
722 #ifdef DEBUG
723         fprintf(stdout, "%s opened...\n", PortDevice);
724 #endif /* DEBUG */
725
726         sig_io.sa_handler = SigHandler;
727         sig_io.sa_flags = 0;
728         sigaction (SIGIO, &sig_io, NULL);
729         /* Allow process/thread to receive SIGIO */
730         fcntl(PortFD, F_SETOWN, getpid());
731 #ifndef VELO
732         /* Make filedescriptor asynchronous. */
733         fcntl(PortFD, F_SETFL, FASYNC);
734 #endif
735         /* Save old termios */
736         tcgetattr(PortFD, &old_termios);
737         /* set speed , 8bit, odd parity */
738         memset( &new_termios, 0, sizeof(new_termios) );
739         new_termios.c_cflag = B9600 | CS8 | CLOCAL | CREAD | PARODD | PARENB; 
740         new_termios.c_iflag = 0;
741         new_termios.c_lflag = 0;
742         new_termios.c_oflag = 0;
743         new_termios.c_cc[VMIN] = 1;
744         new_termios.c_cc[VTIME] = 0;
745         tcflush(PortFD, TCIFLUSH);
746         tcsetattr(PortFD, TCSANOW, &new_termios);
747         /* setting the RTS & DTR bit */
748         flags = TIOCM_DTR;
749         ioctl(PortFD, TIOCMBIS, &flags);
750         flags = TIOCM_RTS;
751         ioctl(PortFD, TIOCMBIS, &flags);
752 #ifdef DEBUG
753         ioctl(PortFD, TIOCMGET, &flags);
754         fprintf(stdout, _("DTR is %s.\n"), flags & TIOCM_DTR ? _("up") : _("down"));
755         fprintf(stdout, _("RTS is %s.\n"), flags & TIOCM_RTS ? _("up") : _("down"));
756         fprintf(stdout, "\n");
757 #endif /* DEBUG */
758         return (true);
759 }
760
761 static GSM_Error
762 SetKey(int c, int up)
763 {
764         u8 reg[] = { 0x7a /* RPC_UI_KEY_PRESS or RPC_UI_KEY_RELEASE */, 0, 1, 0 /* key code */ };
765         reg[0] += up;
766         reg[3] = c;
767         fprintf(stderr, "\n Pressing %d\n", c );
768         PacketOK = false;
769         SendCommand( reg, 0xe5, 4 );
770         while (!PacketOK)
771                 usleep(20000);
772         return GE_NONE;
773 }
774
775 #define XCTRL(a) (a&0x1f)
776 #define POWER XCTRL('o')
777 #define SEND XCTRL('t')
778 #define END XCTRL('s')
779 #define CLR XCTRL('h')
780 #define MENU XCTRL('d')
781 #define ALPHA XCTRL('a')
782 #define PLUS XCTRL('b')
783 #define MINUS XCTRL('e')
784 #define PREV XCTRL('p')
785 #define NEXT XCTRL('n')
786 #define SOFTA XCTRL('x')
787 #define SOFTB XCTRL('q')
788
789 static char lastkey;
790
791 static void PressKey(char c, int i)
792 {
793         lastkey = c;
794 #define X( a, b ) case a: SetKey(b, i); break;
795         switch (c) {
796         case '1' ... '9': SetKey(c-'0',i); break;
797         X('0', 10)
798         X('#', 11)
799         X('*', 12)
800         X(POWER, 13)
801         X(SEND, 14)
802         X(END, 15)
803         X(PLUS, 16)
804         X(MINUS, 17)
805         X(CLR, 18)
806         X(MENU, 21)
807         X(ALPHA, 22)
808         X(PREV, 23)
809         X(NEXT, 24)
810         X(SOFTA, 25)
811         X(SOFTB, 26)
812 #if 0
813         X(STO, 19)      /* These are not present on 2110, so I can't test this. Enable once tested. */
814         X(RCL, 20)
815         X(MUTE, 28)
816 #endif
817         default: fprintf(stderr, "Unknown key %d\n", c);
818         }
819 #undef X
820 }
821
822
823 static void
824 PressString(char *s, int upcase)
825 {
826         static int lastchar;
827         static int lastupcase = 1;
828
829         if (lastchar == *s) {
830                 fprintf(stderr, "***collision");
831                 PressKey(ALPHA, 0);
832                 PressKey(ALPHA, 0);
833                 lastupcase = 1;
834         }
835
836         while (*s) {
837                 lastchar = *s;
838                 PressKey(*s, 0);
839                 if (upcase != lastupcase) {
840                         fprintf(stderr, "***size change");
841                         usleep(1500000);
842                         PressKey(*s, 1);
843                         lastupcase = upcase;
844                 }
845                 s++;
846         }
847 }
848
849 /*
850  * This is able to press keys at 62letters/36seconds, tested on message
851  * "Tohle je testovaci zprava schvalne za jak dlouho ji to napise"
852  */
853 static void
854 HandleKey(char c)
855 {
856         switch(c) {
857 #define X(a, b) case a: PressString(b, 0); break;
858         X('-', "1");
859         X('?', "11");
860         X('!', "111");
861         X(',', "1111");
862         X('.', "11111");
863         X(':', "111111");
864         X('"', "1111111");
865         X('\'', "11111111");
866         X('&', "111111111");
867         X('$', "1111111111");
868 /*      X('$', "11111111111"); libra, not in ascii */
869         X('(', "111111111111");
870         X(')', "1111111111111");
871         X('/', "11111111111111");
872         X('%', "111111111111111");
873         X('@', "1111111111111111");
874         X('_', "11111111111111111");
875         X('=', "111111111111111111");
876         X('a', "2");
877         X('b', "22");
878         X('c', "222");
879         X('d', "3");
880         X('e', "33");
881         X('f', "333");
882         X('g', "4");
883         X('h', "44");
884         X('i', "444");
885         X('j', "5");
886         X('k', "55");
887         X('l', "555");
888         X('m', "6");
889         X('n', "66");
890         X('o', "666");
891         X('p', "7");
892         X('q', "77");
893         X('r', "777");
894         X('s', "7777");
895         X('t', "8");
896         X('u', "88");
897         X('v', "888");
898         X('w', "9");
899         X('x', "99");
900         X('y', "999");
901         X('z', "9999");
902 #undef X
903 #define X(a, b) case a: PressString(b, 1); break;
904         X('A', "2");
905         X('B', "22");
906         X('C', "222");
907         X('D', "3");
908         X('E', "33");
909         X('F', "333");
910         X('G', "4");
911         X('H', "44");
912         X('I', "444");
913         X('J', "5");
914         X('K', "55");
915         X('L', "555");
916         X('M', "6");
917         X('N', "66");
918         X('O', "666");
919         X('P', "7");
920         X('Q', "77");
921         X('R', "777");
922         X('S', "7777");
923         X('T', "8");
924         X('U', "88");
925         X('V', "888");
926         X('W', "9");
927         X('X', "99");
928         X('Y', "999");
929         X('Z', "9999");
930 #undef X
931         case ' ': PressKey('#', 0); break;
932         case '+': PressKey(ALPHA, 0); PressKey('*', 0); PressKey('*', 0);  PressKey(ALPHA, 0); break;
933         case '*': case '#':
934         case '0' ... '9': PressKey(ALPHA, 0); PressKey(c, 0); PressKey(ALPHA, 0); break;
935         default: PressKey(c, 0);
936         }
937 }
938
939 static GSM_Error
940 HandleString(char *s)
941 {
942         while (*s) {
943                 HandleKey(*s);
944                 s++;
945         }
946         fprintf(stderr,"***end of input");
947         PressKey(lastkey, 1);
948         return GE_NONE;
949 }
950
951 static void
952 Register(void)
953 {
954         u8 reg[] = { 1, 1, 0x0f, 1, 0x0f };
955         SendFrame( reg, 0xe9, 5 );
956 }
957
958 /* Fixme: implement grabdisplay properly */
959 static GSM_Error
960 EnableDisplayOutput(void)
961 {
962         /* LN_UC_SHARE, LN_UC_SHARE, LN_UC_RELEASE, LN_UC_RELEASE, LN_UC_KEEP */
963         u8  pkt[] = {3, 3, 0, 0, 1};
964
965         return GE_NOTIMPLEMENTED;       /* We can do grab display, but we do not know how to ungrab it and with
966                                            display grabbed, we can't even initialize connection. */
967         PacketOK = false;
968
969         SendCommand(pkt, 0x19, 5);
970         while(!PacketOK) {
971                 fprintf(stderr, "\nGrabbing display");
972                 usleep(1000000);
973         }
974         if ((PacketData[3] != 0xcd) ||
975             (PacketData[2] != 1) || 
976             (PacketData[4] != 1 /* LN_UC_REQUEST_OK */))
977                 fprintf(stderr, "Something is very wrong with GrabDisplay\n");
978         fprintf(stderr, "Display grabbed okay\n");
979         return GE_NONE;
980 }
981
982
983 /* This is the main loop for the MB21 functions.  When MB21_Initialise
984            is called a thread is created to run this loop.  This loop is
985            exited when the application calls the MB21_Terminate function. */
986 static void
987 ThreadLoop(void)
988 {
989         fprintf(stderr, "Initializing... ");
990         /* Do initialisation stuff */
991         LastChar = GetTime();
992         if (OpenSerial() != true) {
993                 MB21_LinkOK = false;
994 #ifdef THREAD
995                 while (!RequestTerminate) {
996                         usleep (100000);
997                 }
998 #endif
999                 return;
1000         }
1001
1002         usleep(100000);
1003         while(!MB21_LinkOK) {
1004                 fprintf(stderr, "registration... ");
1005                 Register();
1006                 msleep(100);
1007                 msleep(100);
1008                 msleep(100);
1009                 msleep(100);
1010         }
1011         fprintf(stderr, "okay\n");
1012 #ifdef THREAD
1013         while (!RequestTerminate)
1014                 msleep(100);            /* Avoid becoming a "busy" loop. */
1015 #endif
1016 }
1017
1018 /* Initialise variables and state machine. */
1019 static GSM_Error   
1020 Initialise(char *port_device, char *initlength, GSM_ConnectionType connection,
1021                        void (*rlp_callback)(RLP_F96Frame *frame))
1022 {
1023         RequestTerminate = false;
1024         MB21_LinkOK     = false;
1025         memset(VersionInfo,0,sizeof(VersionInfo));
1026         strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
1027 #ifdef THREAD
1028         {
1029                 int rtn;
1030                 rtn = pthread_create(&Thread, NULL, (void *) ThreadLoop, (void *)NULL);
1031                 if(rtn == EAGAIN || rtn == EINVAL)
1032                         return (GE_INTERNALERROR);
1033         }
1034 #else
1035         ThreadLoop();
1036 #endif
1037         return (GE_NONE);
1038 }
1039
1040 /* Routine to get specifed phone book location.  Designed to be called by
1041    application.  Will block until location is retrieved or a timeout/error
1042    occurs. */
1043
1044 static GSM_Error
1045 GetPhonebookLocation(GSM_PhonebookEntry *entry)
1046 {
1047         u8  pkt[] = {0x1a, 0 /* 1 == phone */, 0};
1048         int i;
1049
1050         pkt[1] = 3 + (entry->MemoryType != GMT_ME);
1051         pkt[2] = entry->Location;
1052         
1053         PacketOK = false;
1054         SendCommand(pkt, /* LN_LOC_COMMAND */ 0x1f, 3);
1055         while (!PacketOK)
1056                 msleep(100);
1057         if ((PacketData[3] != 0xc9) ||
1058             (PacketData[4] != 0x1a)) {
1059                 fprintf(stderr, "Something is very wrong with GetPhonebookLocation\n");
1060                 return GE_BUSY;
1061         }
1062         dprintf("type= %x\n", PacketData[5]);
1063         dprintf("location= %x\n", PacketData[6]);
1064         dprintf("status= %x\n", PacketData[7]);
1065         for (i=8; PacketData[i]; i++) {
1066                 dprintf("%c", PacketData[i]);
1067         }
1068         strcpy(entry->Name, (void *)&PacketData[8]);
1069         i++;
1070         strcpy(entry->Number, (void *)&PacketData[i]);
1071         for (; PacketData[i]; i++) {
1072                 dprintf("%c", PacketData[i]);
1073         }
1074         dprintf("\n");
1075         entry->Empty = false;
1076         entry->Group = 0;
1077
1078         return (GE_NONE);
1079 }
1080
1081 /* Routine to write phonebook location in phone. Designed to be called by
1082    application code. Will block until location is written or timeout
1083    occurs. */
1084
1085 static GSM_Error
1086 WritePhonebookLocation(GSM_PhonebookEntry *entry)
1087 {
1088         u8  pkt[999] = {0x1b, 0 /* 1 == phone */, 0};
1089
1090         pkt[1] = 3 + (entry->MemoryType != GMT_ME);
1091         pkt[2] = entry->Location;
1092         strcpy(&pkt[3], entry->Name);
1093         strcpy(&pkt[3+strlen(entry->Name)+1], entry->Number);
1094         
1095         PacketOK = false;
1096         SendCommand(pkt, /* LN_LOC_COMMAND */ 0x1f, 3+strlen(entry->Number)+strlen(entry->Name)+2);
1097         while(!PacketOK) {
1098                 usleep(1000000);
1099         }
1100         printf("okay?\n");
1101         if ((PacketData[3] != 0xc9) ||
1102             (PacketData[4] != 0x1b)) {
1103                 fprintf(stderr, "Something is very wrong with WritePhonebookLocation\n");
1104                 return GE_BUSY;
1105         }
1106         printf("type= %x\n", PacketData[5]);
1107         printf("location= %x\n", PacketData[6]);
1108         printf("status= %x\n", PacketData[7]);
1109         return (GE_NONE);
1110 }
1111
1112 static GSM_Error
1113 GetSMSStatus(GSM_SMSStatus *Status)
1114 {
1115         Status->UnRead = 0;
1116         Status->Number = 5;
1117         return GE_NONE;
1118 }
1119
1120
1121 GSM_Functions MB21_Functions = {
1122         Initialise,
1123         Terminate,
1124         GetPhonebookLocation,
1125         WritePhonebookLocation,
1126         UNIMPLEMENTED,
1127         UNIMPLEMENTED,
1128         GetMemoryStatus,
1129         GetSMSStatus,
1130         UNIMPLEMENTED,
1131         UNIMPLEMENTED,
1132         GetSMSMessage,
1133         DeleteSMSMessage,
1134         UNIMPLEMENTED,
1135         UNIMPLEMENTED,
1136         GetRFLevel,
1137         GetBatteryLevel,
1138         UNIMPLEMENTED,
1139         UNIMPLEMENTED,
1140         UNIMPLEMENTED,
1141         UNIMPLEMENTED,
1142         UNIMPLEMENTED,
1143         GetRevision,
1144         GetModel2110,
1145         PNOK_GetManufacturer,
1146         UNIMPLEMENTED,
1147         UNIMPLEMENTED,
1148         UNIMPLEMENTED,
1149         UNIMPLEMENTED,
1150         UNIMPLEMENTED,
1151         UNIMPLEMENTED,
1152         UNIMPLEMENTED,
1153         UNIMPLEMENTED,
1154         UNIMPLEMENTED,
1155         UNIMPLEMENTED,
1156         UNIMPLEMENTED,
1157         UNIMPLEMENTED,
1158         UNIMPLEMENTED,
1159         UNIMPLEMENTED,
1160         UNIMPLEMENTED,
1161         UNIMPLEMENTED,
1162         UNIMPLEMENTED,
1163         UNIMPLEMENTED,
1164         UNIMPLEMENTED,
1165         UNIMPLEMENTED,
1166         SendRLPFrame,
1167         UNIMPLEMENTED,
1168         EnableDisplayOutput,
1169         UNIMPLEMENTED,
1170         UNIMPLEMENTED,
1171         UNIMPLEMENTED,
1172         UNIMPLEMENTED,
1173         SetKey,
1174         HandleString,
1175         UNIMPLEMENTED
1176 };
1177
1178 #endif
1179