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.2 2002/04/03 00:07:59 short
17 Found in "gnokii-working" directory, some November-patches version
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,
119 /* FIXME - these are guesses only... */
120 GSM_Information MB640_Information = {
122 4, /* Max RF Level */
123 0, /* Min RF Level */
124 GRF_Arbitrary, /* RF level units */
125 4, /* Max Battery Level */
126 0, /* Min Battery Level */
127 GBU_Arbitrary, /* Battery level units */
128 GDT_None, /* No date/time support */
129 GDT_None, /* No alarm support */
130 0, /* Max alarms = 0 */
131 0, 0, /* Startup logo size */
132 0, 0, /* Op logo size */
133 0, 0 /* Caller logo size */
136 char MB640_2_KOI8[] =
138 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
139 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
140 ' ','!','"','#','$','&','%','\'','(',')','*','+',',','-','.','/',
141 '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
143 '!','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
144 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
145 '?','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
146 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',' ',
147 ' ',' ',' ','á','â','÷','ç','ä','å','ö','ú','é','ê','ë','ì','í',
148 'î','ï','ð','ò','ó','ô','õ','æ','è','ã','þ','û','ý','ÿ','ù','ø',
149 ' ',' ',' ','Á','Â','×','Ç','Ä','Å','Ö','Ú','É','Ê','Ë','Ì','Í',
150 'Î','Ï','Ð','Ò','Ó','Ô','Õ','Æ','È','Ã','Þ','Û','Ý','ß','Ù','Ø',
152 ' ','ü','à','ñ',' ','Ü','À','Ñ',' ',' ',' ',' ',' ',' ',' ',' ',
153 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
154 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
155 ' ',' ',' ',' ',' ',' ',' ',' ','E',' ',' ','@','$','L','Y',' ',
158 char KOI8_2_MB640[] =
160 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
161 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
162 ' ','!','"','#',0xFC,'&','%','\'','(',')','*','+',',','-','.','/',
163 '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
165 0xFB,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
166 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
167 '?','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
168 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',' ',
170 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
171 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
172 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
173 ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
174 /* À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï*/
175 0xC6,0xA3,0xA4,0xB9,0xA7,0xA8,0xB7,0xA6,0xB8,0xAB,0xBC,0xAD,0xAE,0xAF,0xB0,0xB1,
176 /* Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß*/
177 0xB2,0xC7,0xB3,0xB4,0xB5,0xB6,0xA9,0xA5,0xBD,0xBE,0xAA,0xBB,0xC5,0xBC,0xBA,0xBF,
178 /* à á â ã ä å æ ç è é ê ë ì í î ï*/
179 0xC2,0x83,0x84,0x99,0x87,0x88,0x97,0x86,0x98,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,
180 /* ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ*/
181 0x92,0xC3,0x93,0x94,0x95,0x96,0x89,0x85,0x9D,0x9E,0x8A,0x9B,0xC1,0x9C,0x9A,0x9F,
184 /* Local variables */
186 bool RequestTerminate;
188 struct termios old_termios; /* old termios */
189 u8 MB640_TXPacketNumber = 0x00;
190 char Model[MB640_MAX_MODEL_LENGTH];
191 bool ModelValid = false;
192 unsigned char PacketData[256];
193 bool MB640_ACKOK = false,
194 MB640_PacketOK = false,
195 MB640_EchoOK = false;
197 /* The following functions are made visible to gsm-api.c and friends. */
199 /* Initialise variables and state machine. */
200 GSM_Error MB640_Initialise(char *port_device, char *initlength,
201 GSM_ConnectionType connection,
202 void (*rlp_callback)(RLP_F96Frame *frame))
205 /* ConnectionType is ignored in 640 code. */
206 RequestTerminate = false;
207 MB640_LinkOK = false;
208 memset(MB640_VersionInfo,0,sizeof(MB640_VersionInfo));
209 strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
210 rtn = pthread_create(&Thread, NULL, (void *) MB640_ThreadLoop, (void *)NULL);
211 if(rtn == EAGAIN || rtn == EINVAL)
213 return (GE_INTERNALERROR);
218 /* Applications should call MB640_Terminate to close the serial port. */
219 void MB640_Terminate(void)
221 /* Request termination of thread */
222 RequestTerminate = true;
223 /* Now wait for thread to terminate. */
224 pthread_join(Thread, NULL);
225 /* Close serial port. */
228 tcsetattr(PortFD, TCSANOW, &old_termios);
233 /* Routine to get specifed phone book location. Designed to
234 be called by application. Will block until location is
235 retrieved or a timeout/error occurs. */
236 GSM_Error MB640_GetMemoryLocation(GSM_PhonebookEntry *entry)
238 u8 pkt[] = {0x0f, 0x2d, 3, 0, 7, 0x1f, 0x7f, 0xf0, 0, 0, 0, 0}, digit;
239 char *digit_map = " 1234567890*#pw+";
241 if(!entry->Location) return (GE_INVALIDPHBOOKLOCATION);
242 switch(entry->MemoryType)
245 /* if(entry->Location > 100) return (GE_INVALIDPHBOOKLOCATION); */
246 pkt[9] = entry->Location - 1;
249 if(entry->Location > 5) return (GE_INVALIDPHBOOKLOCATION);
250 pkt[9] = entry->Location + 99;
253 if(entry->Location > 1) return (GE_INVALIDPHBOOKLOCATION);
256 default: return (GE_INVALIDMEMORYTYPE);
259 MB640_PacketOK = false;
262 while(!MB640_PacketOK)
264 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
265 if(!--timeout || RequestTerminate)
271 entry->Empty = (PacketData[18] == 0 && PacketData[34] == 0);
274 for( i = 0; PacketData[34 + i] && i < 16; i++ )
276 entry->Name[i] = MB640_2_KOI8[ PacketData[34 + i] ];
280 len = PacketData[18];
281 for( i = 0; i < len; i++ )
283 digit = PacketData[19 + i/2];
284 entry->Number[i] = digit_map[((i % 2) ? digit : digit >> 4) & 0x0F];
286 entry->Number[i] = 0;
287 entry->Group = PacketData[50];
292 entry->Number[0] = 0;
298 /* Routine to write phonebook location in phone. Designed to
299 be called by application code. Will block until location
300 is written or timeout occurs. */
301 GSM_Error MB640_WritePhonebookLocation(GSM_PhonebookEntry *entry)
304 switch(entry->MemoryType)
319 pkt[9] = entry->Location - 1;
324 memset(&pkt[14],0,32);
325 pkt[46] = 0x05/*entry->Group*/;
327 for( i = 0; entry->Name[i] && i < 15; i++ )
329 pkt[30 + i] = KOI8_2_MB640[ (u8)entry->Name[i] ];
334 for( i = 0, s = entry->Number; *s && i < 30; s++ )
338 case '1'...'9': digit = *s - '0'; break;
339 case '0': digit = 0xA; break;
340 case '*': digit = 0xB; break;
341 case '#': digit = 0xC; break;
342 case 'p': digit = 0xD; break;
343 case 'w': digit = 0xE; break;
344 case '+': digit = 0xF; break;
347 pkt[15 + i/2] |= (i % 2) ? digit : digit << 4;
353 MB640_PacketOK = false;
356 while(!MB640_PacketOK)
358 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
359 if(!--timeout || RequestTerminate)
367 default: return (GE_NOTIMPLEMENTED);
374 For now, GetRFLevel and GetBatteryLevel both rely
375 on data returned by the "keepalive" packets. I suspect
376 that we don't actually need the keepalive at all but
377 will await the official doco before taking it out. HAB19990511 */
378 GSM_Error MB640_GetRFLevel(GSM_RFUnits *units, float *level)
380 /*{0x0f, 0x5A, 3, 0, 7, 0x39, 0x7f, 0xf0, 0, 0, 0, 0}*/
385 MB640_PacketOK = false;
388 while(!MB640_PacketOK)
390 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
391 if(!--timeout || RequestTerminate)
397 *level = 0/*(float)(PacketData[6] * 256 + PacketData[7]) / 256.0*/;
401 /* MB640_GetBatteryLevel - get value from ADC #0
402 FIXME (see above...) */
403 GSM_Error MB640_GetBatteryLevel(GSM_BatteryUnits *units, float *level)
404 {u8 pkt[] = {0x19, 2, 1, 0};
407 if (*units == GBU_Arbitrary)
409 MB640_PacketOK = false;
412 while(!MB640_PacketOK)
414 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
415 if(!--timeout || RequestTerminate)
421 *level = (float)(PacketData[6] * 256 + PacketData[7]) / 256.0;
424 return (GE_INTERNALERROR);
427 /* Really there are no IMEI in NMT phones. Equivalent IMHO is phone
429 GSM_Error MB640_GetIMEI(char *imei)
430 {u8 pkt[] = {0x0f, 0x19, 3, 0, 0x01, 0x0b, 0, 0};
433 MB640_PacketOK = false;
436 while(!MB640_PacketOK)
438 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
439 if(!--timeout || RequestTerminate)
446 memcpy(imei,&PacketData[14],PacketData[13]);
447 imei[PacketData[13]] = 0;
451 GSM_Error MB640_GetVersionInfo()
452 {u8 pkt[] = {0, 3, 0};
454 char *s = MB640_VersionInfo;
456 MB640_PacketOK = false;
459 while(!MB640_PacketOK)
461 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
462 if(!--timeout || RequestTerminate)
469 strncpy( s, &PacketData[6], sizeof(MB640_VersionInfo) );
471 for( MB640_Revision = s; *s != 0x0A; s++ ) if( !*s ) goto out;
473 for( MB640_RevisionDate = s; *s != 0x0A; s++ ) if( !*s ) goto out;
475 for( MB640_Model = s; *s != 0x0A; s++ ) if( !*s ) goto out;
481 GSM_Error MB640_GetRevision(char *revision)
482 {GSM_Error err = GE_NONE;
484 if(!MB640_Revision) err = MB640_GetVersionInfo();
485 if(err == GE_NONE) strncpy(revision, MB640_Revision, 64);
490 GSM_Error MB640_GetModel(char *model)
491 {GSM_Error err = GE_NONE;
493 if(!MB640_Model) err = MB640_GetVersionInfo();
494 if(err == GE_NONE) strncpy(model, MB640_Model, 64);
499 GSM_Error MB640_GetMemoryStatus(GSM_MemoryStatus *Status)
501 switch(Status->MemoryType)
519 default: return (GE_NOTIMPLEMENTED);
524 GSM_Error MB640_GetBitmap(GSM_Bitmap *Bitmap)
529 case GSM_StartupLogo:
530 {u8 pkt[] = {0x0f, 0x60, 3, 0, 7, 0x3A, 0x7f, 0xf0, 0, 0, 0, 0};
533 for(i = 0; i < 6; i++)
536 MB640_PacketOK = false;
539 while(!MB640_PacketOK)
541 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
542 if(!--timeout || RequestTerminate)
548 memcpy(&Bitmap->bitmap[i * 84],&PacketData[18],84);
552 Bitmap->size = 84*48/8;
555 default: return (GE_NOTIMPLEMENTED);
560 GSM_Error MB640_SetBitmap(GSM_Bitmap *Bitmap)
565 case GSM_StartupLogo:
582 for(i = 0; i < 6; i++)
585 memcpy(&pkt[14],&Bitmap->bitmap[i * 84],84);
587 MB640_PacketOK = false;
590 while(!MB640_PacketOK)
592 if(!MB640_ACKOK) MB640_SendPacket(pkt, sizeof(pkt));
593 if(!--timeout || RequestTerminate)
602 default: return (GE_NOTIMPLEMENTED);
607 GSM_Error MB640_Reset(unsigned char type)
608 {u8 pkt[] = { 0x43, 0x00, 0x00 };
612 MB640_PacketOK = false;
615 while(!MB640_PacketOK)
617 if(!MB640_ACKOK) MB640_SendPacket(pkt, 4);
618 if(!--timeout) return (GE_TIMEOUT);
624 bool MB640_SendRLPFrame(RLP_F96Frame *frame, bool out_dtx)
629 /* Everything from here down is internal to 640 code. */
631 /* Checksum calculation */
632 u8 MB640_GetChecksum( u8 * packet )
634 unsigned int i,len = packet[2];
636 if( packet[2] == 0x7F ) len = 4; /* ACK packet length */
637 else len += 5; /* Add sizes of header, command and *
638 * packet_number to packet length */
639 for( i = 0; i < len; i++ ) checksum ^= packet[i]; /* calculate checksum */
643 /* Handler called when characters received from serial port.
644 * and process them. */
645 void MB640_SigHandler(int status)
646 {unsigned char buffer[256],ack[5],b;
648 static unsigned int Index = 0,
650 static unsigned char pkt[256];
655 res = read(PortFD, buffer, 256);
656 if( res < 0 ) return;
657 for(i = 0; i < res ; i++)
660 if(!Index && b != 0x00 && b != 0xE9)
662 /* something strange goes from phone. Just ignore it */
664 fprintf( stdout, "Get [%02X %c]\n", b, b >= 0x20 ? b : '.' );
671 if(Index == 3) Length = (b == 0x7F) ? 5 : b + 6;
674 if(pkt[0] == 0xE9 && pkt[1] == 0x00) /* packet from phone */
677 fprintf( stdout, _("Phone: ") );
678 for( j = 0; j < Length; j++ )
681 fprintf( stdout, "[%02X %c]", b, b >= 0x20 ? b : '.' );
683 fprintf( stdout, "\n" );
685 /* ensure that we received valid packet */
686 if(pkt[Length - 1] == MB640_GetChecksum(pkt))
688 if(pkt[2] == 0x7F) /* acknowledge by phone */
692 /* Increase TX packet number */
693 MB640_TXPacketNumber++;
697 /* Copy packet data */
698 memcpy(PacketData,pkt,Length);
699 /* send acknowledge packet to phone */
701 ack[0] = 0x00; /* Construct the header. */
702 ack[1] = pkt[0]; /* Send back id */
703 ack[2] = 0x7F; /* Set special size value */
704 ack[3] = pkt[Length - 2]; /* Send back packet number */
705 ack[4] = MB640_GetChecksum( ack ); /* Set checksum */
707 fprintf( stdout, _("PC : ") );
708 for( j = 0; j < 5; j++ )
711 fprintf( stdout, "[%02X %c]", b, b >= 0x20 ? b : '.' );
713 fprintf( stdout, "\n" );
715 if( write( PortFD, ack, 5 ) != 5 )
717 perror( _("Write error!\n") );
719 /* Set validity flag */
720 MB640_PacketOK = true;
728 /* Look for new packet */
736 /* Called by initialisation code to open comm port in asynchronous mode. */
737 bool MB640_OpenSerial(void)
739 struct termios new_termios;
740 struct sigaction sig_io;
744 fprintf(stdout, _("Setting MBUS communication...\n"));
747 PortFD = open(PortDevice, O_RDWR | O_NOCTTY | O_NONBLOCK);
750 fprintf(stderr, "Failed to open %s ...\n", PortDevice);
755 fprintf(stdout, "%s opened...\n", PortDevice);
758 sig_io.sa_handler = MB640_SigHandler;
760 sigaction (SIGIO, &sig_io, NULL);
761 /* Allow process/thread to receive SIGIO */
762 fcntl(PortFD, F_SETOWN, getpid());
763 /* Make filedescriptor asynchronous. */
764 fcntl(PortFD, F_SETFL, FASYNC);
765 /* Save old termios */
766 tcgetattr(PortFD, &old_termios);
767 /* set speed , 8bit, odd parity */
768 memset( &new_termios, 0, sizeof(new_termios) );
769 new_termios.c_cflag = B9600 | CS8 | CLOCAL | CREAD | PARODD | PARENB;
770 new_termios.c_iflag = 0;
771 new_termios.c_lflag = 0;
772 new_termios.c_oflag = 0;
773 new_termios.c_cc[VMIN] = 1;
774 new_termios.c_cc[VTIME] = 0;
775 tcflush(PortFD, TCIFLUSH);
776 tcsetattr(PortFD, TCSANOW, &new_termios);
777 /* setting the RTS & DTR bit */
779 ioctl(PortFD, TIOCMBIC, &flags);
781 ioctl(PortFD, TIOCMBIS, &flags);
783 ioctl(PortFD, TIOCMGET, &flags);
784 fprintf(stdout, _("DTR is %s.\n"), flags & TIOCM_DTR ? _("up") : _("down"));
785 fprintf(stdout, _("RTS is %s.\n"), flags & TIOCM_RTS ? _("up") : _("down"));
786 fprintf(stdout, "\n");
791 GSM_Error MB640_SendPacket( u8 *buffer, u8 length )
795 /* FIXME - we should check for the message length ... */
796 pkt[current++] = 0x00; /* Construct the header. */
797 pkt[current++] = 0xE9;
798 pkt[current++] = length; /* Set data size */
799 pkt[current++] = 0xE5;
800 memcpy( pkt + current, buffer, length ); /* Copy in data. */
802 pkt[current++] = MB640_TXPacketNumber; /* Set packet number */
803 pkt[current++] = MB640_GetChecksum( pkt ); /* Calculate and set checksum */
807 fprintf( stdout, _("PC : ") );
808 for( i = 0; i < current; i++ )
811 fprintf( stdout, "[%02X %c]", b, b > 0x20 ? b : '.' );
813 fprintf( stdout, "\n" );
817 MB640_EchoOK = false;
818 if( write(PortFD, pkt, current) != current )
820 perror( _("Write error!\n") );
821 return (GE_INTERNALERROR);
824 while( !MB640_EchoOK && current-- )
828 if( !MB640_EchoOK ) return (GE_TIMEOUT);
833 /* This is the main loop for the MB21 functions. When MB21_Initialise
834 is called a thread is created to run this loop. This loop is
835 exited when the application calls the MB21_Terminate function. */
836 void MB640_ThreadLoop(void)
838 /* Do initialisation stuff */
839 if (MB640_OpenSerial() != true)
841 MB640_LinkOK = false;
842 while (!RequestTerminate)
850 while (!RequestTerminate) {
851 usleep(100000); /* Avoid becoming a "busy" loop. */