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.4.1 2001/11/27 20:19:50 short
18 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
21 Revision 1.1.1.1.2.1 2001/11/27 04:37:59 short
22 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
24 Revision 1.1.1.2 2001/11/27 04:19:30 short
25 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 05:17 CET 2001
27 Revision 1.7 2001/11/26 18:06:08 pkot
28 Checking for *printf functions, N_(x) for localization, generic ARRAY_LEN, SAFE_STRNCPY, G_GNUC_PRINTF (Jan Kratochvil)
30 Revision 1.6 2001/11/19 13:03:18 pkot
33 Revision 1.5 2001/11/08 16:49:19 pkot
36 Revision 1.4 2001/08/20 23:36:27 pkot
37 More cleanup in AT code (Manfred Jonsson)
39 Revision 1.3 2001/08/20 23:27:37 pkot
40 Add hardware shakehand to the link layer (Manfred Jonsson)
42 Revision 1.2 2001/08/09 11:51:39 pkot
43 Generic AT support updates and cleanup (Manfred Jonsson)
45 Revision 1.1 2001/07/27 00:02:21 pkot
46 Generic AT support for the new structure (Manfred Jonsson)
55 #include "gsm-common.h"
56 #include "gsm-statemachine.h"
57 #include "gsm-encoding.h"
58 #include "phones/generic.h"
59 #include "phones/atgen.h"
60 #include "phones/ateric.h"
61 #include "phones/atsie.h"
62 #include "phones/atnok.h"
63 #include "links/atbus.h"
64 #include "links/cbus.h"
67 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
68 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
69 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
70 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
71 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
72 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
73 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
74 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
76 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
77 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
78 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
79 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
80 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
81 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
82 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
83 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
84 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
88 AT_SendFunctionType sfunc;
89 GSM_RecvFunctionType rfunc;
90 } AT_FunctionInitType;
93 /* Mobile phone information */
94 static AT_SendFunctionType AT_Functions[GOP_Max];
95 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
96 static AT_FunctionInitType AT_FunctionInit[] = {
97 { GOP_Init, NULL, Reply },
98 { GOP_GetModel, AT_GetModel, ReplyIdentify },
99 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
100 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
101 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
102 { GOP_Identify, AT_Identify, ReplyIdentify },
103 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
104 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
105 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
106 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
107 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
111 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
112 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
115 GSM_Phone phone_at = {
117 PGEN_IncomingDefault,
119 "AT|AT-HW|dancall", /* Supported models */
120 99, /* Max RF Level */
121 0, /* Min RF Level */
122 GRF_CSQ, /* RF level units */
123 100, /* Max Battery Level */
124 0, /* Min Battery Level */
125 GBU_Percentage, /* Battery level units */
126 0, /* Have date/time support */
127 0, /* Alarm supports time only */
128 0, /* Alarms available - FIXME */
129 0, 0, /* Startup logo size - FIXME */
130 0, 0, /* Op logo size */
131 0, 0 /* Caller logo size */
137 static GSM_MemoryType memorytype = GMT_XX;
138 static int atcharset = 0;
140 static char *memorynames[] = {
141 "ME", /* Internal memory of the mobile equipment */
142 "SM", /* SIM card memory */
143 "FD", /* Fixed dial numbers */
144 "ON", /* Own numbers */
145 "EN", /* Emergency numbers */
146 "DC", /* Dialled numbers */
147 "RC", /* Received numbers */
148 "MC", /* Missed numbers */
149 "LD", /* Last dialed */
150 "MT", /* combined ME and SIM phonebook */
151 "TA", /* for compatibility only: TA=computer memory */
152 "CB", /* Currently selected memory */
155 /* LinkOK is always true for now... */
156 bool ATGEN_LinkOK = true;
159 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
163 GSM_RecvFunctionType oldfunc;
165 if (type >= GOP_Max) {
166 return (GSM_RecvFunctionType) -1;
169 IncomingFunctions[pos].MessageType = type;
170 IncomingFunctions[pos].Functions = func;
174 for (i=0; i < pos; i++) {
175 if (IncomingFunctions[i].MessageType == type) {
176 oldfunc = IncomingFunctions[i].Functions;
177 IncomingFunctions[i].Functions = func;
181 if (pos < GOP_Max-1) {
182 IncomingFunctions[pos].MessageType = type;
183 IncomingFunctions[pos].Functions = func;
190 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
192 AT_SendFunctionType f;
194 f = AT_Functions[type];
195 AT_Functions[type] = func;
200 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
204 sprintf(req, "ATE1\r\n");
205 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
206 return SM_Block(state, data, GOP_Init);
210 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
213 GSM_Error ret = GE_NONE;
216 if (mt != memorytype) {
217 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
218 ret = SM_SendMessage(state, 14, GOP_Init, req);
221 GSM_DataClear(&data);
222 ret = SM_Block(state, &data, GOP_Init);
230 static GSM_Error SetCharset(GSM_Statemachine *state)
233 GSM_Error ret = GE_NONE;
236 if (atcharset == 0) {
237 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
238 ret = SM_SendMessage(state, 15, GOP_Init, req);
241 GSM_DataClear(&data);
242 ret = SM_Block(state, &data, GOP_Init);
250 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
254 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
256 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
258 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
260 return Functions(GOP_GetImei, data, state);
264 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
268 sprintf(req, "AT+CGMM\r\n");
269 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
271 return SM_Block(state, data, GOP_Identify);
275 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
279 sprintf(req, "AT+CGMI\r\n");
280 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
282 return SM_Block(state, data, GOP_Identify);
286 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
290 sprintf(req, "AT+CGMR\r\n");
291 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
293 return SM_Block(state, data, GOP_Identify);
297 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
301 sprintf(req, "AT+CGSN\r\n");
302 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
304 return SM_Block(state, data, GOP_Identify);
308 /* gets battery level and power source */
310 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
314 sprintf(req, "AT+CBC\r\n");
315 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
317 return SM_Block(state, data, GOP_GetBatteryLevel);
321 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
325 sprintf(req, "AT+CSQ\r\n");
326 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
328 return SM_Block(state, data, GOP_GetRFLevel);
332 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
337 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
340 sprintf(req, "AT+CPBS?\r\n");
341 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
343 ret = SM_Block(state, data, GOP_GetMemoryStatus);
344 if (ret != GE_UNKNOWN)
346 sprintf(req, "AT+CPBR=?\r\n");
347 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
349 ret = SM_Block(state, data, GOP_GetMemoryStatus);
354 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
359 ret = SetCharset(state);
362 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
365 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
366 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
368 ret = SM_Block(state, data, GOP_ReadPhonebook);
373 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
376 return Initialise(data, state);
377 if ((op > GOP_Init) && (op < GOP_Max))
378 if (AT_Functions[op])
379 return (*AT_Functions[op])(data, state);
380 return GE_NOTIMPLEMENTED;
384 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
393 if (buf.line1 == NULL)
394 return GE_INVALIDPHBOOKLOCATION;
396 if (strncmp(buffer, "AT+CPBR", 7)) {
397 return GE_NONE; /*FIXME*/
400 if (!strncmp(buf.line2, "OK", 2)) {
401 if (data->PhonebookEntry) {
402 *(data->PhonebookEntry->Number) = '\0';
403 *(data->PhonebookEntry->Name) = '\0';
404 data->PhonebookEntry->Group = 0;
405 data->PhonebookEntry->SubEntriesCount = 0;
409 if (data->PhonebookEntry) {
410 data->PhonebookEntry->Group = 0;
411 data->PhonebookEntry->SubEntriesCount = 0;
412 pos = strchr(buf.line2, '\"');
415 endpos = strchr(++pos, '\"');
418 strcpy(data->PhonebookEntry->Number, pos);
422 pos = strchr(++endpos, '\"');
426 l = pos - (char *)buffer;
427 endpos = memchr(pos, '\"', length - l);
431 DecodeAscii(data->PhonebookEntry->Name, pos, l);
432 *(data->PhonebookEntry->Name + l) = '\0';
439 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
447 if (buf.line1 == NULL)
448 return GE_INVALIDMEMORYTYPE;
450 if (data->MemoryStatus) {
451 if (strstr(buf.line2,"+CPBS")) {
452 pos = strchr(buf.line2, ',');
454 data->MemoryStatus->Used = atoi(++pos);
456 data->MemoryStatus->Used = 100;
457 data->MemoryStatus->Free = 0;
460 pos = strchr(pos, ',');
462 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
472 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
480 if ((buf.line1 == NULL) || (buf.line2 == NULL))
483 if (!strncmp(buffer, "AT+CBC", 6)) {
484 if (data->BatteryLevel) {
485 *(data->BatteryUnits) = GBU_Percentage;
486 pos = strchr(buf.line2, ',');
489 *(data->BatteryLevel) = atoi(pos);
491 *(data->BatteryLevel) = 1;
494 if (data->PowerSource) {
495 *(data->PowerSource) = 0;
496 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
497 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
504 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
512 if (buf.line1 == NULL)
515 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
516 *(data->RFUnits) = GRF_CSQ;
517 pos1 = buf.line2 + 6;
518 pos2 = strchr(buf.line2, ',');
520 *(data->RFLevel) = atoi(pos1);
522 *(data->RFLevel) = 1;
529 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
536 if (buf.line1 == NULL)
537 return GE_NONE; /* Fixme */
538 if (!strncmp(buffer, "AT+CG", 5)) {
539 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
540 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
541 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
542 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
548 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
556 if (buf.line1 == NULL)
563 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
568 char manufacturer[20];
571 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
573 /* Copy in the phone info */
574 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
576 for (i=0; i<GOP_Max; i++) {
577 AT_Functions[i] = NULL;
578 IncomingFunctions[i].MessageType = 0;
579 IncomingFunctions[i].Functions = NULL;
581 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
582 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
583 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
586 switch (state->Link.ConnectionType) {
588 if (!strcmp(setupdata->Model, "dancall"))
589 CBUS_Initialise(state);
590 else if (!strcmp(setupdata->Model, "AT-HW"))
591 ATBUS_Initialise(state, true);
593 ATBUS_Initialise(state, false);
596 return GE_NOTSUPPORTED;
599 SM_Initialise(state);
601 SetEcho(&data, state);
603 GSM_DataClear(&data);
605 ret = state->Phone.Functions(GOP_GetModel, &data, state);
606 if (ret != GE_NONE) return ret;
607 GSM_DataClear(&data);
608 data.Manufacturer = manufacturer;
609 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
610 if (ret != GE_NONE) return ret;
612 if (!strncasecmp(manufacturer, "ericsson", 8))
613 AT_InitEricsson(state, model, setupdata->Model);
614 if (!strncasecmp(manufacturer, "siemens", 7))
615 AT_InitSiemens(state, model, setupdata->Model);
616 if (!strncasecmp(manufacturer, "nokia", 5))
617 AT_InitNokia(state, model, setupdata->Model);
623 void splitlines(AT_LineBuffer *buf)
627 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
631 pos = findcrlf(buf->line1, 0, buf->length);
634 buf->line2 = skipcrlf(++pos);
636 buf->line2 = buf->line1;
638 pos = findcrlf(buf->line2, 1, buf->length);
641 buf->line3 = skipcrlf(++pos);
643 buf->line3 = buf->line2;
649 * increments the argument until a char unequal to
650 * <cr> or <lf> is found. returns the new position.
653 char *skipcrlf(char *str)
657 while ((*str == '\n') || (*str == '\r'))
664 * searches for <cr> or <lf> and returns the first
665 * occurrence. if test is set, the gsm char @ which
666 * is 0x00 is not considered as end of string.
667 * return NULL if no <cr> or <lf> was found in the
668 * range of max bytes.
671 char *findcrlf(char *str, int test, int max)
675 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
679 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))