7 A Linux/Unix toolset and driver for mobile phones.
9 Copyright 2001 Manfred Jonsson <manfred.jonsson@gmx.de>
11 Released under the terms of the GNU GPL, see file COPYING for more details.
13 This file provides functions specific to generic at command compatible
14 phones. See README for more details on supported mobile phones.
17 Revision 1.1.1.1 2001/11/25 21:59:11 short
18 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
20 Revision 1.6 2001/11/19 13:03:18 pkot
23 Revision 1.5 2001/11/08 16:49:19 pkot
26 Revision 1.4 2001/08/20 23:36:27 pkot
27 More cleanup in AT code (Manfred Jonsson)
29 Revision 1.3 2001/08/20 23:27:37 pkot
30 Add hardware shakehand to the link layer (Manfred Jonsson)
32 Revision 1.2 2001/08/09 11:51:39 pkot
33 Generic AT support updates and cleanup (Manfred Jonsson)
35 Revision 1.1 2001/07/27 00:02:21 pkot
36 Generic AT support for the new structure (Manfred Jonsson)
45 #include "gsm-common.h"
46 #include "gsm-statemachine.h"
47 #include "gsm-encoding.h"
48 #include "phones/generic.h"
49 #include "phones/atgen.h"
50 #include "phones/ateric.h"
51 #include "phones/atsie.h"
52 #include "phones/atnok.h"
53 #include "links/atbus.h"
54 #include "links/cbus.h"
57 #define ARRAY_LEN(x) (sizeof((x))/sizeof((x)[0]))
60 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
61 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
62 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
63 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
64 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
65 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
66 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
67 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
69 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
70 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
71 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
72 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
73 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
74 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
75 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
76 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
77 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
81 AT_SendFunctionType sfunc;
82 GSM_RecvFunctionType rfunc;
83 } AT_FunctionInitType;
86 /* Mobile phone information */
87 static AT_SendFunctionType AT_Functions[GOP_Max];
88 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
89 static AT_FunctionInitType AT_FunctionInit[] = {
90 { GOP_Init, NULL, Reply },
91 { GOP_GetModel, AT_GetModel, ReplyIdentify },
92 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
93 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
94 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
95 { GOP_Identify, AT_Identify, ReplyIdentify },
96 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
97 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
98 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
99 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
100 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
104 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
105 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
108 GSM_Phone phone_at = {
110 PGEN_IncomingDefault,
112 "AT|AT-HW|dancall", /* Supported models */
113 99, /* Max RF Level */
114 0, /* Min RF Level */
115 GRF_CSQ, /* RF level units */
116 100, /* Max Battery Level */
117 0, /* Min Battery Level */
118 GBU_Percentage, /* Battery level units */
119 0, /* Have date/time support */
120 0, /* Alarm supports time only */
121 0, /* Alarms available - FIXME */
122 0, 0, /* Startup logo size - FIXME */
123 0, 0, /* Op logo size */
124 0, 0 /* Caller logo size */
130 static GSM_MemoryType memorytype = GMT_XX;
131 static int atcharset = 0;
133 static char *memorynames[] = {
134 "ME", /* Internal memory of the mobile equipment */
135 "SM", /* SIM card memory */
136 "FD", /* Fixed dial numbers */
137 "ON", /* Own numbers */
138 "EN", /* Emergency numbers */
139 "DC", /* Dialled numbers */
140 "RC", /* Received numbers */
141 "MC", /* Missed numbers */
142 "LD", /* Last dialed */
143 "MT", /* combined ME and SIM phonebook */
144 "TA", /* for compatibility only: TA=computer memory */
145 "CB", /* Currently selected memory */
148 /* LinkOK is always true for now... */
149 bool ATGEN_LinkOK = true;
152 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
156 GSM_RecvFunctionType oldfunc;
158 if (type >= GOP_Max) {
159 return (GSM_RecvFunctionType) -1;
162 IncomingFunctions[pos].MessageType = type;
163 IncomingFunctions[pos].Functions = func;
167 for (i=0; i < pos; i++) {
168 if (IncomingFunctions[i].MessageType == type) {
169 oldfunc = IncomingFunctions[i].Functions;
170 IncomingFunctions[i].Functions = func;
174 if (pos < GOP_Max-1) {
175 IncomingFunctions[pos].MessageType = type;
176 IncomingFunctions[pos].Functions = func;
183 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
185 AT_SendFunctionType f;
187 f = AT_Functions[type];
188 AT_Functions[type] = func;
193 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
197 sprintf(req, "ATE1\r\n");
198 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
199 return SM_Block(state, data, GOP_Init);
203 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
206 GSM_Error ret = GE_NONE;
209 if (mt != memorytype) {
210 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
211 ret = SM_SendMessage(state, 14, GOP_Init, req);
214 GSM_DataClear(&data);
215 ret = SM_Block(state, &data, GOP_Init);
223 static GSM_Error SetCharset(GSM_Statemachine *state)
226 GSM_Error ret = GE_NONE;
229 if (atcharset == 0) {
230 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
231 ret = SM_SendMessage(state, 15, GOP_Init, req);
234 GSM_DataClear(&data);
235 ret = SM_Block(state, &data, GOP_Init);
243 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
247 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
249 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
251 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
253 return Functions(GOP_GetImei, data, state);
257 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
261 sprintf(req, "AT+CGMM\r\n");
262 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
264 return SM_Block(state, data, GOP_Identify);
268 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
272 sprintf(req, "AT+CGMI\r\n");
273 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
275 return SM_Block(state, data, GOP_Identify);
279 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
283 sprintf(req, "AT+CGMR\r\n");
284 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
286 return SM_Block(state, data, GOP_Identify);
290 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
294 sprintf(req, "AT+CGSN\r\n");
295 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
297 return SM_Block(state, data, GOP_Identify);
301 /* gets battery level and power source */
303 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
307 sprintf(req, "AT+CBC\r\n");
308 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
310 return SM_Block(state, data, GOP_GetBatteryLevel);
314 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
318 sprintf(req, "AT+CSQ\r\n");
319 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
321 return SM_Block(state, data, GOP_GetRFLevel);
325 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
330 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
333 sprintf(req, "AT+CPBS?\r\n");
334 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
336 ret = SM_Block(state, data, GOP_GetMemoryStatus);
337 if (ret != GE_UNKNOWN)
339 sprintf(req, "AT+CPBR=?\r\n");
340 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
342 ret = SM_Block(state, data, GOP_GetMemoryStatus);
347 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
352 ret = SetCharset(state);
355 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
358 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
359 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
361 ret = SM_Block(state, data, GOP_ReadPhonebook);
366 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
369 return Initialise(data, state);
370 if ((op > GOP_Init) && (op < GOP_Max))
371 if (AT_Functions[op])
372 return (*AT_Functions[op])(data, state);
373 return GE_NOTIMPLEMENTED;
377 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
386 if (buf.line1 == NULL)
387 return GE_INVALIDPHBOOKLOCATION;
389 if (strncmp(buffer, "AT+CPBR", 7)) {
390 return GE_NONE; /*FIXME*/
393 if (!strncmp(buf.line2, "OK", 2)) {
394 if (data->PhonebookEntry) {
395 *(data->PhonebookEntry->Number) = '\0';
396 *(data->PhonebookEntry->Name) = '\0';
397 data->PhonebookEntry->Group = 0;
398 data->PhonebookEntry->SubEntriesCount = 0;
402 if (data->PhonebookEntry) {
403 data->PhonebookEntry->Group = 0;
404 data->PhonebookEntry->SubEntriesCount = 0;
405 pos = strchr(buf.line2, '\"');
408 endpos = strchr(++pos, '\"');
411 strcpy(data->PhonebookEntry->Number, pos);
415 pos = strchr(++endpos, '\"');
419 l = pos - (char *)buffer;
420 endpos = memchr(pos, '\"', length - l);
424 DecodeAscii(data->PhonebookEntry->Name, pos, l);
425 *(data->PhonebookEntry->Name + l) = '\0';
432 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
440 if (buf.line1 == NULL)
441 return GE_INVALIDMEMORYTYPE;
443 if (data->MemoryStatus) {
444 if (strstr(buf.line2,"+CPBS")) {
445 pos = strchr(buf.line2, ',');
447 data->MemoryStatus->Used = atoi(++pos);
449 data->MemoryStatus->Used = 100;
450 data->MemoryStatus->Free = 0;
453 pos = strchr(pos, ',');
455 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
465 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
473 if ((buf.line1 == NULL) || (buf.line2 == NULL))
476 if (!strncmp(buffer, "AT+CBC", 6)) {
477 if (data->BatteryLevel) {
478 *(data->BatteryUnits) = GBU_Percentage;
479 pos = strchr(buf.line2, ',');
482 *(data->BatteryLevel) = atoi(pos);
484 *(data->BatteryLevel) = 1;
487 if (data->PowerSource) {
488 *(data->PowerSource) = 0;
489 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
490 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
497 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
505 if (buf.line1 == NULL)
508 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
509 *(data->RFUnits) = GRF_CSQ;
510 pos1 = buf.line2 + 6;
511 pos2 = strchr(buf.line2, ',');
513 *(data->RFLevel) = atoi(pos1);
515 *(data->RFLevel) = 1;
522 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
529 if (buf.line1 == NULL)
530 return GE_NONE; /* Fixme */
531 if (!strncmp(buffer, "AT+CG", 5)) {
532 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
533 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
534 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
535 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
541 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
549 if (buf.line1 == NULL)
556 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
561 char manufacturer[20];
564 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
566 /* Copy in the phone info */
567 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
569 for (i=0; i<GOP_Max; i++) {
570 AT_Functions[i] = NULL;
571 IncomingFunctions[i].MessageType = 0;
572 IncomingFunctions[i].Functions = NULL;
574 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
575 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
576 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
579 switch (state->Link.ConnectionType) {
581 if (!strcmp(setupdata->Model, "dancall"))
582 CBUS_Initialise(state);
583 else if (!strcmp(setupdata->Model, "AT-HW"))
584 ATBUS_Initialise(state, true);
586 ATBUS_Initialise(state, false);
589 return GE_NOTSUPPORTED;
592 SM_Initialise(state);
594 SetEcho(&data, state);
596 GSM_DataClear(&data);
598 ret = state->Phone.Functions(GOP_GetModel, &data, state);
599 if (ret != GE_NONE) return ret;
600 GSM_DataClear(&data);
601 data.Manufacturer = manufacturer;
602 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
603 if (ret != GE_NONE) return ret;
605 if (!strncasecmp(manufacturer, "ericsson", 8))
606 AT_InitEricsson(state, model, setupdata->Model);
607 if (!strncasecmp(manufacturer, "siemens", 7))
608 AT_InitSiemens(state, model, setupdata->Model);
609 if (!strncasecmp(manufacturer, "nokia", 5))
610 AT_InitNokia(state, model, setupdata->Model);
616 void splitlines(AT_LineBuffer *buf)
620 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
624 pos = findcrlf(buf->line1, 0, buf->length);
627 buf->line2 = skipcrlf(++pos);
629 buf->line2 = buf->line1;
631 pos = findcrlf(buf->line2, 1, buf->length);
634 buf->line3 = skipcrlf(++pos);
636 buf->line3 = buf->line2;
642 * increments the argument until a char unequal to
643 * <cr> or <lf> is found. returns the new position.
646 char *skipcrlf(char *str)
650 while ((*str == '\n') || (*str == '\r'))
657 * searches for <cr> or <lf> and returns the first
658 * occurrence. if test is set, the gsm char @ which
659 * is 0x00 is not considered as end of string.
660 * return NULL if no <cr> or <lf> was found in the
661 * range of max bytes.
664 char *findcrlf(char *str, int test, int max)
668 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
672 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))