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.2.1 2001/11/27 04:37:59 short
18 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
20 Revision 1.1.1.2 2001/11/27 04:19:30 short
21 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 05:17 CET 2001
23 Revision 1.7 2001/11/26 18:06:08 pkot
24 Checking for *printf functions, N_(x) for localization, generic ARRAY_LEN, SAFE_STRNCPY, G_GNUC_PRINTF (Jan Kratochvil)
26 Revision 1.6 2001/11/19 13:03:18 pkot
29 Revision 1.5 2001/11/08 16:49:19 pkot
32 Revision 1.4 2001/08/20 23:36:27 pkot
33 More cleanup in AT code (Manfred Jonsson)
35 Revision 1.3 2001/08/20 23:27:37 pkot
36 Add hardware shakehand to the link layer (Manfred Jonsson)
38 Revision 1.2 2001/08/09 11:51:39 pkot
39 Generic AT support updates and cleanup (Manfred Jonsson)
41 Revision 1.1 2001/07/27 00:02:21 pkot
42 Generic AT support for the new structure (Manfred Jonsson)
51 #include "gsm-common.h"
52 #include "gsm-statemachine.h"
53 #include "gsm-encoding.h"
54 #include "phones/generic.h"
55 #include "phones/atgen.h"
56 #include "phones/ateric.h"
57 #include "phones/atsie.h"
58 #include "phones/atnok.h"
59 #include "links/atbus.h"
60 #include "links/cbus.h"
63 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
64 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
65 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
66 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
67 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
68 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
69 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
70 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
72 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
73 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
74 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
75 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
76 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
77 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
78 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
79 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
80 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
84 AT_SendFunctionType sfunc;
85 GSM_RecvFunctionType rfunc;
86 } AT_FunctionInitType;
89 /* Mobile phone information */
90 static AT_SendFunctionType AT_Functions[GOP_Max];
91 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
92 static AT_FunctionInitType AT_FunctionInit[] = {
93 { GOP_Init, NULL, Reply },
94 { GOP_GetModel, AT_GetModel, ReplyIdentify },
95 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
96 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
97 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
98 { GOP_Identify, AT_Identify, ReplyIdentify },
99 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
100 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
101 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
102 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
103 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
107 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
108 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
111 GSM_Phone phone_at = {
113 PGEN_IncomingDefault,
115 "AT|AT-HW|dancall", /* Supported models */
116 99, /* Max RF Level */
117 0, /* Min RF Level */
118 GRF_CSQ, /* RF level units */
119 100, /* Max Battery Level */
120 0, /* Min Battery Level */
121 GBU_Percentage, /* Battery level units */
122 0, /* Have date/time support */
123 0, /* Alarm supports time only */
124 0, /* Alarms available - FIXME */
125 0, 0, /* Startup logo size - FIXME */
126 0, 0, /* Op logo size */
127 0, 0 /* Caller logo size */
133 static GSM_MemoryType memorytype = GMT_XX;
134 static int atcharset = 0;
136 static char *memorynames[] = {
137 "ME", /* Internal memory of the mobile equipment */
138 "SM", /* SIM card memory */
139 "FD", /* Fixed dial numbers */
140 "ON", /* Own numbers */
141 "EN", /* Emergency numbers */
142 "DC", /* Dialled numbers */
143 "RC", /* Received numbers */
144 "MC", /* Missed numbers */
145 "LD", /* Last dialed */
146 "MT", /* combined ME and SIM phonebook */
147 "TA", /* for compatibility only: TA=computer memory */
148 "CB", /* Currently selected memory */
151 /* LinkOK is always true for now... */
152 bool ATGEN_LinkOK = true;
155 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
159 GSM_RecvFunctionType oldfunc;
161 if (type >= GOP_Max) {
162 return (GSM_RecvFunctionType) -1;
165 IncomingFunctions[pos].MessageType = type;
166 IncomingFunctions[pos].Functions = func;
170 for (i=0; i < pos; i++) {
171 if (IncomingFunctions[i].MessageType == type) {
172 oldfunc = IncomingFunctions[i].Functions;
173 IncomingFunctions[i].Functions = func;
177 if (pos < GOP_Max-1) {
178 IncomingFunctions[pos].MessageType = type;
179 IncomingFunctions[pos].Functions = func;
186 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
188 AT_SendFunctionType f;
190 f = AT_Functions[type];
191 AT_Functions[type] = func;
196 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
200 sprintf(req, "ATE1\r\n");
201 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
202 return SM_Block(state, data, GOP_Init);
206 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
209 GSM_Error ret = GE_NONE;
212 if (mt != memorytype) {
213 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
214 ret = SM_SendMessage(state, 14, GOP_Init, req);
217 GSM_DataClear(&data);
218 ret = SM_Block(state, &data, GOP_Init);
226 static GSM_Error SetCharset(GSM_Statemachine *state)
229 GSM_Error ret = GE_NONE;
232 if (atcharset == 0) {
233 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
234 ret = SM_SendMessage(state, 15, GOP_Init, req);
237 GSM_DataClear(&data);
238 ret = SM_Block(state, &data, GOP_Init);
246 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
250 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
252 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
254 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
256 return Functions(GOP_GetImei, data, state);
260 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
264 sprintf(req, "AT+CGMM\r\n");
265 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
267 return SM_Block(state, data, GOP_Identify);
271 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
275 sprintf(req, "AT+CGMI\r\n");
276 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
278 return SM_Block(state, data, GOP_Identify);
282 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
286 sprintf(req, "AT+CGMR\r\n");
287 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
289 return SM_Block(state, data, GOP_Identify);
293 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
297 sprintf(req, "AT+CGSN\r\n");
298 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
300 return SM_Block(state, data, GOP_Identify);
304 /* gets battery level and power source */
306 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
310 sprintf(req, "AT+CBC\r\n");
311 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
313 return SM_Block(state, data, GOP_GetBatteryLevel);
317 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
321 sprintf(req, "AT+CSQ\r\n");
322 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
324 return SM_Block(state, data, GOP_GetRFLevel);
328 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
333 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
336 sprintf(req, "AT+CPBS?\r\n");
337 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
339 ret = SM_Block(state, data, GOP_GetMemoryStatus);
340 if (ret != GE_UNKNOWN)
342 sprintf(req, "AT+CPBR=?\r\n");
343 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
345 ret = SM_Block(state, data, GOP_GetMemoryStatus);
350 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
355 ret = SetCharset(state);
358 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
361 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
362 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
364 ret = SM_Block(state, data, GOP_ReadPhonebook);
369 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
372 return Initialise(data, state);
373 if ((op > GOP_Init) && (op < GOP_Max))
374 if (AT_Functions[op])
375 return (*AT_Functions[op])(data, state);
376 return GE_NOTIMPLEMENTED;
380 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
389 if (buf.line1 == NULL)
390 return GE_INVALIDPHBOOKLOCATION;
392 if (strncmp(buffer, "AT+CPBR", 7)) {
393 return GE_NONE; /*FIXME*/
396 if (!strncmp(buf.line2, "OK", 2)) {
397 if (data->PhonebookEntry) {
398 *(data->PhonebookEntry->Number) = '\0';
399 *(data->PhonebookEntry->Name) = '\0';
400 data->PhonebookEntry->Group = 0;
401 data->PhonebookEntry->SubEntriesCount = 0;
405 if (data->PhonebookEntry) {
406 data->PhonebookEntry->Group = 0;
407 data->PhonebookEntry->SubEntriesCount = 0;
408 pos = strchr(buf.line2, '\"');
411 endpos = strchr(++pos, '\"');
414 strcpy(data->PhonebookEntry->Number, pos);
418 pos = strchr(++endpos, '\"');
422 l = pos - (char *)buffer;
423 endpos = memchr(pos, '\"', length - l);
427 DecodeAscii(data->PhonebookEntry->Name, pos, l);
428 *(data->PhonebookEntry->Name + l) = '\0';
435 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
443 if (buf.line1 == NULL)
444 return GE_INVALIDMEMORYTYPE;
446 if (data->MemoryStatus) {
447 if (strstr(buf.line2,"+CPBS")) {
448 pos = strchr(buf.line2, ',');
450 data->MemoryStatus->Used = atoi(++pos);
452 data->MemoryStatus->Used = 100;
453 data->MemoryStatus->Free = 0;
456 pos = strchr(pos, ',');
458 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
468 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
476 if ((buf.line1 == NULL) || (buf.line2 == NULL))
479 if (!strncmp(buffer, "AT+CBC", 6)) {
480 if (data->BatteryLevel) {
481 *(data->BatteryUnits) = GBU_Percentage;
482 pos = strchr(buf.line2, ',');
485 *(data->BatteryLevel) = atoi(pos);
487 *(data->BatteryLevel) = 1;
490 if (data->PowerSource) {
491 *(data->PowerSource) = 0;
492 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
493 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
500 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
508 if (buf.line1 == NULL)
511 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
512 *(data->RFUnits) = GRF_CSQ;
513 pos1 = buf.line2 + 6;
514 pos2 = strchr(buf.line2, ',');
516 *(data->RFLevel) = atoi(pos1);
518 *(data->RFLevel) = 1;
525 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
532 if (buf.line1 == NULL)
533 return GE_NONE; /* Fixme */
534 if (!strncmp(buffer, "AT+CG", 5)) {
535 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
536 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
537 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
538 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
544 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
552 if (buf.line1 == NULL)
559 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
564 char manufacturer[20];
567 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
569 /* Copy in the phone info */
570 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
572 for (i=0; i<GOP_Max; i++) {
573 AT_Functions[i] = NULL;
574 IncomingFunctions[i].MessageType = 0;
575 IncomingFunctions[i].Functions = NULL;
577 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
578 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
579 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
582 switch (state->Link.ConnectionType) {
584 if (!strcmp(setupdata->Model, "dancall"))
585 CBUS_Initialise(state);
586 else if (!strcmp(setupdata->Model, "AT-HW"))
587 ATBUS_Initialise(state, true);
589 ATBUS_Initialise(state, false);
592 return GE_NOTSUPPORTED;
595 SM_Initialise(state);
597 SetEcho(&data, state);
599 GSM_DataClear(&data);
601 ret = state->Phone.Functions(GOP_GetModel, &data, state);
602 if (ret != GE_NONE) return ret;
603 GSM_DataClear(&data);
604 data.Manufacturer = manufacturer;
605 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
606 if (ret != GE_NONE) return ret;
608 if (!strncasecmp(manufacturer, "ericsson", 8))
609 AT_InitEricsson(state, model, setupdata->Model);
610 if (!strncasecmp(manufacturer, "siemens", 7))
611 AT_InitSiemens(state, model, setupdata->Model);
612 if (!strncasecmp(manufacturer, "nokia", 5))
613 AT_InitNokia(state, model, setupdata->Model);
619 void splitlines(AT_LineBuffer *buf)
623 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
627 pos = findcrlf(buf->line1, 0, buf->length);
630 buf->line2 = skipcrlf(++pos);
632 buf->line2 = buf->line1;
634 pos = findcrlf(buf->line2, 1, buf->length);
637 buf->line3 = skipcrlf(++pos);
639 buf->line3 = buf->line2;
645 * increments the argument until a char unequal to
646 * <cr> or <lf> is found. returns the new position.
649 char *skipcrlf(char *str)
653 while ((*str == '\n') || (*str == '\r'))
660 * searches for <cr> or <lf> and returns the first
661 * occurrence. if test is set, the gsm char @ which
662 * is 0x00 is not considered as end of string.
663 * return NULL if no <cr> or <lf> was found in the
664 * range of max bytes.
667 char *findcrlf(char *str, int test, int max)
671 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
675 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))