7 A Linux/Unix toolset and driver for Nokia mobile phones.
9 Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
11 Released under the terms of the GNU GPL, see file COPYING for more details.
13 This is the main part of 640 support.
16 Revision 1.1.1.1 2001/11/25 21:59:06 short
17 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
19 Revision 1.22 2001/06/28 00:28:45 pkot
20 Small docs updates (Pawel Kot)
27 #define __mbus_640_c /* "Turn on" prototypes in mbus-640.h */
38 #include <sys/types.h>
40 #include <sys/ioctl.h>
43 # include <sys/file.h>
50 #include "gsm-common.h"
52 #include "phones/nokia.h"
54 /* Global variables used by code in gsm-api.c to expose the
55 functions supported by this model of phone. */
57 char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
58 char *MB640_Revision = 0,
59 *MB640_RevisionDate = 0,
61 MB640_VersionInfo[64];
63 GSM_Functions MB640_Functions = {
66 MB640_GetMemoryLocation,
67 MB640_WritePhonebookLocation,
70 MB640_GetMemoryStatus,
79 MB640_GetBatteryLevel,
120 /* FIXME - these are guesses only... */
121 GSM_Information MB640_Information = {
123 4, /* Max RF Level */
124 0, /* Min RF Level */
125 GRF_Arbitrary, /* RF level units */
126 4, /* Max Battery Level */
127 0, /* Min Battery Level */
128 GBU_Arbitrary, /* Battery level units */
129 GDT_None, /* No date/time support */
130 GDT_None, /* No alarm support */
131 0, /* Max alarms = 0 */
132 0, 0, /* Startup logo size */
133 0, 0, /* Op logo size */
134 0, 0 /* Caller logo size */
137 char MB640_2_KOI8[] =
139 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
140 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
141 ' ','!','"','#','$','&','%','\'','(',')','*','+',',','-','.','/',
142 '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
144 '!','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
145 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
146 '?','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
147 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',' ',
148 ' ',' ',' ','á','â','÷','ç','ä','å','ö','ú','é','ê','ë','ì','í',
149 'î','ï','ð','ò','ó','ô','õ','æ','è','ã','þ','û','ý','ÿ','ù','ø',
150 ' ',' ',' ','Á','Â','×','Ç','Ä','Å','Ö','Ú','É','Ê','Ë','Ì','Í',
151 'Î','Ï','Ð','Ò','Ó','Ô','Õ','Æ','È','Ã','Þ','Û','Ý','ß','Ù','Ø',
153 ' ','ü','à','ñ',' ','Ü','À','Ñ',' ',' ',' ',' ',' ',' ',' ',' ',
154 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
155 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
156 ' ',' ',' ',' ',' ',' ',' ',' ','E',' ',' ','@','$','L','Y',' ',
159 char KOI8_2_MB640[] =
161 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
162 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
163 ' ','!','"','#',0xFC,'&','%','\'','(',')','*','+',',','-','.','/',
164 '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
166 0xFB,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
167 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
168 '?','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
169 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',' ',
171 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
172 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
173 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
174 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
175 /* À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï*/
176 0xC6,0xA3,0xA4,0xB9,0xA7,0xA8,0xB7,0xA6,0xB8,0xAB,0xBC,0xAD,0xAE,0xAF,0xB0,0xB1,
177 /* Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß*/
178 0xB2,0xC7,0xB3,0xB4,0xB5,0xB6,0xA9,0xA5,0xBD,0xBE,0xAA,0xBB,0xC5,0xBC,0xBA,0xBF,
179 /* à á â ã ä å æ ç è é ê ë ì í î ï*/
180 0xC2,0x83,0x84,0x99,0x87,0x88,0x97,0x86,0x98,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,
181 /* ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ*/
182 0x92,0xC3,0x93,0x94,0x95,0x96,0x89,0x85,0x9D,0x9E,0x8A,0x9B,0xC1,0x9C,0x9A,0x9F,
185 /* Local variables */
187 bool RequestTerminate;
189 struct termios old_termios; /* old termios */
190 u8 MB640_TXPacketNumber = 0x00;
191 char Model[MB640_MAX_MODEL_LENGTH];
192 bool ModelValid = false;
193 unsigned char PacketData[256];
194 bool MB640_ACKOK = false,
195 MB640_PacketOK = false,
196 MB640_EchoOK = false;
198 /* The following functions are made visible to gsm-api.c and friends. */
200 /* Initialise variables and state machine. */
201 GSM_Error MB640_Initialise(char *port_device, char *initlength,
202 GSM_ConnectionType connection,
203 void (*rlp_callback)(RLP_F96Frame *frame))
206 /* ConnectionType is ignored in 640 code. */
207 RequestTerminate = false;
208 MB640_LinkOK = false;
209 memset(MB640_VersionInfo,0,sizeof(MB640_VersionInfo));
210 strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
211 rtn = pthread_create(&Thread, NULL, (void *) MB640_ThreadLoop, (void *)NULL);
212 if(rtn == EAGAIN || rtn == EINVAL)
214 return (GE_INTERNALERROR);
219 /* Applications should call MB640_Terminate to close the serial port. */
220 void MB640_Terminate(void)
222 /* Request termination of thread */
223 RequestTerminate = true;
224 /* Now wait for thread to terminate. */
225 pthread_join(Thread, NULL);
226 /* Close serial port. */
229 tcsetattr(PortFD, TCSANOW, &old_termios);
234 /* Routine to get specifed phone book location. Designed to
235 be called by application. Will block until location is
236 retrieved or a timeout/error occurs. */
237 GSM_Error MB640_GetMemoryLocation(GSM_PhonebookEntry *entry)
239 u8 pkt[] = {0x0f, 0x2d, 3, 0, 7, 0x1f, 0x7f, 0xf0, 0, 0, 0, 0}, digit;
240 char *digit_map = " 1234567890*#pw+";
242 if(!entry->Location) return (GE_INVALIDPHBOOKLOCATION);
243 switch(entry->MemoryType)
246 /* if(entry->Location > 100) return (GE_INVALIDPHBOOKLOCATION); */
247 pkt[9] = entry->Location - 1;
250 if(entry->Location > 5) return (GE_INVALIDPHBOOKLOCATION);
251 pkt[9] = entry->Location + 99;
254 if(entry->Location > 1) return (GE_INVALIDPHBOOKLOCATION);
257 default: return (GE_INVALIDMEMORYTYPE);
260 MB640_PacketOK = false;
263 while(!MB640_PacketOK)
265 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
266 if(!--timeout || RequestTerminate)
272 entry->Empty = (PacketData[18] == 0 && PacketData[34] == 0);
275 for( i = 0; PacketData[34 + i] && i < 16; i++ )
277 entry->Name[i] = MB640_2_KOI8[ PacketData[34 + i] ];
281 len = PacketData[18];
282 for( i = 0; i < len; i++ )
284 digit = PacketData[19 + i/2];
285 entry->Number[i] = digit_map[((i % 2) ? digit : digit >> 4) & 0x0F];
287 entry->Number[i] = 0;
288 entry->Group = PacketData[50];
293 entry->Number[0] = 0;
299 /* Routine to write phonebook location in phone. Designed to
300 be called by application code. Will block until location
301 is written or timeout occurs. */
302 GSM_Error MB640_WritePhonebookLocation(GSM_PhonebookEntry *entry)
305 switch(entry->MemoryType)
320 pkt[9] = entry->Location - 1;
325 memset(&pkt[14],0,32);
326 pkt[46] = 0x05/*entry->Group*/;
328 for( i = 0; entry->Name[i] && i < 15; i++ )
330 pkt[30 + i] = KOI8_2_MB640[ (u8)entry->Name[i] ];
335 for( i = 0, s = entry->Number; *s && i < 30; s++ )
339 case '1'...'9': digit = *s - '0'; break;
340 case '0': digit = 0xA; break;
341 case '*': digit = 0xB; break;
342 case '#': digit = 0xC; break;
343 case 'p': digit = 0xD; break;
344 case 'w': digit = 0xE; break;
345 case '+': digit = 0xF; break;
348 pkt[15 + i/2] |= (i % 2) ? digit : digit << 4;
354 MB640_PacketOK = false;
357 while(!MB640_PacketOK)
359 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
360 if(!--timeout || RequestTerminate)
368 default: return (GE_NOTIMPLEMENTED);
375 For now, GetRFLevel and GetBatteryLevel both rely
376 on data returned by the "keepalive" packets. I suspect
377 that we don't actually need the keepalive at all but
378 will await the official doco before taking it out. HAB19990511 */
379 GSM_Error MB640_GetRFLevel(GSM_RFUnits *units, float *level)
381 /*{0x0f, 0x5A, 3, 0, 7, 0x39, 0x7f, 0xf0, 0, 0, 0, 0}*/
386 MB640_PacketOK = false;
389 while(!MB640_PacketOK)
391 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
392 if(!--timeout || RequestTerminate)
398 *level = 0/*(float)(PacketData[6] * 256 + PacketData[7]) / 256.0*/;
402 /* MB640_GetBatteryLevel - get value from ADC #0
403 FIXME (see above...) */
404 GSM_Error MB640_GetBatteryLevel(GSM_BatteryUnits *units, float *level)
405 {u8 pkt[] = {0x19, 2, 1, 0};
408 if (*units == GBU_Arbitrary)
410 MB640_PacketOK = false;
413 while(!MB640_PacketOK)
415 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
416 if(!--timeout || RequestTerminate)
422 *level = (float)(PacketData[6] * 256 + PacketData[7]) / 256.0;
425 return (GE_INTERNALERROR);
428 /* Really there are no IMEI in NMT phones. Equivalent IMHO is phone
430 GSM_Error MB640_GetIMEI(char *imei)
431 {u8 pkt[] = {0x0f, 0x19, 3, 0, 0x01, 0x0b, 0, 0};
434 MB640_PacketOK = false;
437 while(!MB640_PacketOK)
439 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
440 if(!--timeout || RequestTerminate)
447 memcpy(imei,&PacketData[14],PacketData[13]);
448 imei[PacketData[13]] = 0;
452 GSM_Error MB640_GetVersionInfo()
453 {u8 pkt[] = {0, 3, 0};
455 char *s = MB640_VersionInfo;
457 MB640_PacketOK = false;
460 while(!MB640_PacketOK)
462 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
463 if(!--timeout || RequestTerminate)
470 strncpy( s, &PacketData[6], sizeof(MB640_VersionInfo) );
472 for( MB640_Revision = s; *s != 0x0A; s++ ) if( !*s ) goto out;
474 for( MB640_RevisionDate = s; *s != 0x0A; s++ ) if( !*s ) goto out;
476 for( MB640_Model = s; *s != 0x0A; s++ ) if( !*s ) goto out;
482 GSM_Error MB640_GetRevision(char *revision)
483 {GSM_Error err = GE_NONE;
485 if(!MB640_Revision) err = MB640_GetVersionInfo();
486 if(err == GE_NONE) strncpy(revision, MB640_Revision, 64);
491 GSM_Error MB640_GetModel(char *model)
492 {GSM_Error err = GE_NONE;
494 if(!MB640_Model) err = MB640_GetVersionInfo();
495 if(err == GE_NONE) strncpy(model, MB640_Model, 64);
500 GSM_Error MB640_GetMemoryStatus(GSM_MemoryStatus *Status)
502 switch(Status->MemoryType)
520 default: return (GE_NOTIMPLEMENTED);
525 GSM_Error MB640_GetBitmap(GSM_Bitmap *Bitmap)
530 case GSM_StartupLogo:
531 {u8 pkt[] = {0x0f, 0x60, 3, 0, 7, 0x3A, 0x7f, 0xf0, 0, 0, 0, 0};
534 for(i = 0; i < 6; i++)
537 MB640_PacketOK = false;
540 while(!MB640_PacketOK)
542 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
543 if(!--timeout || RequestTerminate)
549 memcpy(&Bitmap->bitmap[i * 84],&PacketData[18],84);
553 Bitmap->size = 84*48/8;
556 default: return (GE_NOTIMPLEMENTED);
561 GSM_Error MB640_SetBitmap(GSM_Bitmap *Bitmap)
566 case GSM_StartupLogo:
583 for(i = 0; i < 6; i++)
586 memcpy(&pkt[14],&Bitmap->bitmap[i * 84],84);
588 MB640_PacketOK = false;
591 while(!MB640_PacketOK)
593 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
594 if(!--timeout || RequestTerminate)
603 default: return (GE_NOTIMPLEMENTED);
608 GSM_Error MB640_Reset(unsigned char type)
609 {u8 pkt[] = { 0x43, 0x00, 0x00 };
613 MB640_PacketOK = false;
616 while(!MB640_PacketOK)
618 if(!MB640_ACKOK) MB640_SendPacket(pkt, 4);
619 if(!--timeout) return (GE_TIMEOUT);
625 bool MB640_SendRLPFrame(RLP_F96Frame *frame, bool out_dtx)
630 /* Everything from here down is internal to 640 code. */
632 /* Checksum calculation */
633 u8 MB640_GetChecksum( u8 * packet )
635 unsigned int i,len = packet[2];
637 if( packet[2] == 0x7F ) len = 4; /* ACK packet length */
638 else len += 5; /* Add sizes of header, command and *
639 * packet_number to packet length */
640 for( i = 0; i < len; i++ ) checksum ^= packet[i]; /* calculate checksum */
644 /* Handler called when characters received from serial port.
645 * and process them. */
646 void MB640_SigHandler(int status)
647 {unsigned char buffer[256],ack[5],b;
649 static unsigned int Index = 0,
651 static unsigned char pkt[256];
656 res = read(PortFD, buffer, 256);
657 if( res < 0 ) return;
658 for(i = 0; i < res ; i++)
661 if(!Index && b != 0x00 && b != 0xE9)
663 /* something strange goes from phone. Just ignore it */
665 fprintf( stdout, "Get [%02X %c]\n", b, b >= 0x20 ? b : '.' );
672 if(Index == 3) Length = (b == 0x7F) ? 5 : b + 6;
675 if(pkt[0] == 0xE9 && pkt[1] == 0x00) /* packet from phone */
678 fprintf( stdout, _("Phone: ") );
679 for( j = 0; j < Length; j++ )
682 fprintf( stdout, "[%02X %c]", b, b >= 0x20 ? b : '.' );
684 fprintf( stdout, "\n" );
686 /* ensure that we received valid packet */
687 if(pkt[Length - 1] == MB640_GetChecksum(pkt))
689 if(pkt[2] == 0x7F) /* acknowledge by phone */
693 /* Increase TX packet number */
694 MB640_TXPacketNumber++;
698 /* Copy packet data */
699 memcpy(PacketData,pkt,Length);
700 /* send acknowledge packet to phone */
702 ack[0] = 0x00; /* Construct the header. */
703 ack[1] = pkt[0]; /* Send back id */
704 ack[2] = 0x7F; /* Set special size value */
705 ack[3] = pkt[Length - 2]; /* Send back packet number */
706 ack[4] = MB640_GetChecksum( ack ); /* Set checksum */
708 fprintf( stdout, _("PC : ") );
709 for( j = 0; j < 5; j++ )
712 fprintf( stdout, "[%02X %c]", b, b >= 0x20 ? b : '.' );
714 fprintf( stdout, "\n" );
716 if( write( PortFD, ack, 5 ) != 5 )
718 perror( _("Write error!\n") );
720 /* Set validity flag */
721 MB640_PacketOK = true;
729 /* Look for new packet */
737 /* Called by initialisation code to open comm port in asynchronous mode. */
738 bool MB640_OpenSerial(void)
740 struct termios new_termios;
741 struct sigaction sig_io;
745 fprintf(stdout, _("Setting MBUS communication...\n"));
748 PortFD = open(PortDevice, O_RDWR | O_NOCTTY | O_NONBLOCK);
751 fprintf(stderr, "Failed to open %s ...\n", PortDevice);
756 fprintf(stdout, "%s opened...\n", PortDevice);
759 sig_io.sa_handler = MB640_SigHandler;
761 sigaction (SIGIO, &sig_io, NULL);
762 /* Allow process/thread to receive SIGIO */
763 fcntl(PortFD, F_SETOWN, getpid());
764 /* Make filedescriptor asynchronous. */
765 fcntl(PortFD, F_SETFL, FASYNC);
766 /* Save old termios */
767 tcgetattr(PortFD, &old_termios);
768 /* set speed , 8bit, odd parity */
769 memset( &new_termios, 0, sizeof(new_termios) );
770 new_termios.c_cflag = B9600 | CS8 | CLOCAL | CREAD | PARODD | PARENB;
771 new_termios.c_iflag = 0;
772 new_termios.c_lflag = 0;
773 new_termios.c_oflag = 0;
774 new_termios.c_cc[VMIN] = 1;
775 new_termios.c_cc[VTIME] = 0;
776 tcflush(PortFD, TCIFLUSH);
777 tcsetattr(PortFD, TCSANOW, &new_termios);
778 /* setting the RTS & DTR bit */
780 ioctl(PortFD, TIOCMBIC, &flags);
782 ioctl(PortFD, TIOCMBIS, &flags);
784 ioctl(PortFD, TIOCMGET, &flags);
785 fprintf(stdout, _("DTR is %s.\n"), flags & TIOCM_DTR ? _("up") : _("down"));
786 fprintf(stdout, _("RTS is %s.\n"), flags & TIOCM_RTS ? _("up") : _("down"));
787 fprintf(stdout, "\n");
792 GSM_Error MB640_SendPacket( u8 *buffer, u8 length )
796 /* FIXME - we should check for the message length ... */
797 pkt[current++] = 0x00; /* Construct the header. */
798 pkt[current++] = 0xE9;
799 pkt[current++] = length; /* Set data size */
800 pkt[current++] = 0xE5;
801 memcpy( pkt + current, buffer, length ); /* Copy in data. */
803 pkt[current++] = MB640_TXPacketNumber; /* Set packet number */
804 pkt[current++] = MB640_GetChecksum( pkt ); /* Calculate and set checksum */
808 fprintf( stdout, _("PC : ") );
809 for( i = 0; i < current; i++ )
812 fprintf( stdout, "[%02X %c]", b, b > 0x20 ? b : '.' );
814 fprintf( stdout, "\n" );
818 MB640_EchoOK = false;
819 if( write(PortFD, pkt, current) != current )
821 perror( _("Write error!\n") );
822 return (GE_INTERNALERROR);
825 while( !MB640_EchoOK && current-- )
829 if( !MB640_EchoOK ) return (GE_TIMEOUT);
834 /* This is the main loop for the MB21 functions. When MB21_Initialise
835 is called a thread is created to run this loop. This loop is
836 exited when the application calls the MB21_Terminate function. */
837 void MB640_ThreadLoop(void)
839 /* Do initialisation stuff */
840 if (MB640_OpenSerial() != true)
842 MB640_LinkOK = false;
843 while (!RequestTerminate)
851 while (!RequestTerminate) {
852 usleep(100000); /* Avoid becoming a "busy" loop. */