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.12.1 2001/11/27 21:35:51 short
18 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
20 Revision 1.1.1.1.8.1 2001/11/27 21:30:44 short
21 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
23 Revision 1.1.1.1.2.1 2001/11/27 04:37:59 short
24 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
26 Revision 1.1.1.2 2001/11/27 04:19:30 short
27 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 05:17 CET 2001
29 Revision 1.7 2001/11/26 18:06:08 pkot
30 Checking for *printf functions, N_(x) for localization, generic ARRAY_LEN, SAFE_STRNCPY, G_GNUC_PRINTF (Jan Kratochvil)
32 Revision 1.6 2001/11/19 13:03:18 pkot
35 Revision 1.5 2001/11/08 16:49:19 pkot
38 Revision 1.4 2001/08/20 23:36:27 pkot
39 More cleanup in AT code (Manfred Jonsson)
41 Revision 1.3 2001/08/20 23:27:37 pkot
42 Add hardware shakehand to the link layer (Manfred Jonsson)
44 Revision 1.2 2001/08/09 11:51:39 pkot
45 Generic AT support updates and cleanup (Manfred Jonsson)
47 Revision 1.1 2001/07/27 00:02:21 pkot
48 Generic AT support for the new structure (Manfred Jonsson)
57 #include "gsm-common.h"
58 #include "gsm-statemachine.h"
59 #include "gsm-encoding.h"
60 #include "phones/generic.h"
61 #include "phones/atgen.h"
62 #include "phones/ateric.h"
63 #include "phones/atsie.h"
64 #include "phones/atnok.h"
65 #include "links/atbus.h"
66 #include "links/cbus.h"
69 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
70 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
71 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
72 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
73 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
74 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
75 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
76 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
78 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
79 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
80 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
81 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
82 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
83 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
84 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
85 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
86 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
90 AT_SendFunctionType sfunc;
91 GSM_RecvFunctionType rfunc;
92 } AT_FunctionInitType;
95 /* Mobile phone information */
96 static AT_SendFunctionType AT_Functions[GOP_Max];
97 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
98 static AT_FunctionInitType AT_FunctionInit[] = {
99 { GOP_Init, NULL, Reply },
100 { GOP_GetModel, AT_GetModel, ReplyIdentify },
101 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
102 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
103 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
104 { GOP_Identify, AT_Identify, ReplyIdentify },
105 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
106 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
107 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
108 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
109 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
113 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
114 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
117 GSM_Phone phone_at = {
119 PGEN_IncomingDefault,
121 "AT|AT-HW|dancall", /* Supported models */
122 99, /* Max RF Level */
123 0, /* Min RF Level */
124 GRF_CSQ, /* RF level units */
125 100, /* Max Battery Level */
126 0, /* Min Battery Level */
127 GBU_Percentage, /* Battery level units */
128 0, /* Have date/time support */
129 0, /* Alarm supports time only */
130 0, /* Alarms available - FIXME */
131 0, 0, /* Startup logo size - FIXME */
132 0, 0, /* Op logo size */
133 0, 0 /* Caller logo size */
139 static GSM_MemoryType memorytype = GMT_XX;
140 static int atcharset = 0;
142 static char *memorynames[] = {
143 "ME", /* Internal memory of the mobile equipment */
144 "SM", /* SIM card memory */
145 "FD", /* Fixed dial numbers */
146 "ON", /* Own numbers */
147 "EN", /* Emergency numbers */
148 "DC", /* Dialled numbers */
149 "RC", /* Received numbers */
150 "MC", /* Missed numbers */
151 "LD", /* Last dialed */
152 "MT", /* combined ME and SIM phonebook */
153 "TA", /* for compatibility only: TA=computer memory */
154 "CB", /* Currently selected memory */
157 /* LinkOK is always true for now... */
158 bool ATGEN_LinkOK = true;
161 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
165 GSM_RecvFunctionType oldfunc;
167 if (type >= GOP_Max) {
168 return (GSM_RecvFunctionType) -1;
171 IncomingFunctions[pos].MessageType = type;
172 IncomingFunctions[pos].Functions = func;
176 for (i=0; i < pos; i++) {
177 if (IncomingFunctions[i].MessageType == type) {
178 oldfunc = IncomingFunctions[i].Functions;
179 IncomingFunctions[i].Functions = func;
183 if (pos < GOP_Max-1) {
184 IncomingFunctions[pos].MessageType = type;
185 IncomingFunctions[pos].Functions = func;
192 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
194 AT_SendFunctionType f;
196 f = AT_Functions[type];
197 AT_Functions[type] = func;
202 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
206 sprintf(req, "ATE1\r\n");
207 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
208 return SM_Block(state, data, GOP_Init);
212 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
215 GSM_Error ret = GE_NONE;
218 if (mt != memorytype) {
219 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
220 ret = SM_SendMessage(state, 14, GOP_Init, req);
223 GSM_DataClear(&data);
224 ret = SM_Block(state, &data, GOP_Init);
232 static GSM_Error SetCharset(GSM_Statemachine *state)
235 GSM_Error ret = GE_NONE;
238 if (atcharset == 0) {
239 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
240 ret = SM_SendMessage(state, 15, GOP_Init, req);
243 GSM_DataClear(&data);
244 ret = SM_Block(state, &data, GOP_Init);
252 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
256 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
258 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
260 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
262 return Functions(GOP_GetImei, data, state);
266 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
270 sprintf(req, "AT+CGMM\r\n");
271 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
273 return SM_Block(state, data, GOP_Identify);
277 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
281 sprintf(req, "AT+CGMI\r\n");
282 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
284 return SM_Block(state, data, GOP_Identify);
288 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
292 sprintf(req, "AT+CGMR\r\n");
293 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
295 return SM_Block(state, data, GOP_Identify);
299 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
303 sprintf(req, "AT+CGSN\r\n");
304 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
306 return SM_Block(state, data, GOP_Identify);
310 /* gets battery level and power source */
312 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
316 sprintf(req, "AT+CBC\r\n");
317 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
319 return SM_Block(state, data, GOP_GetBatteryLevel);
323 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
327 sprintf(req, "AT+CSQ\r\n");
328 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
330 return SM_Block(state, data, GOP_GetRFLevel);
334 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
339 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
342 sprintf(req, "AT+CPBS?\r\n");
343 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
345 ret = SM_Block(state, data, GOP_GetMemoryStatus);
346 if (ret != GE_UNKNOWN)
348 sprintf(req, "AT+CPBR=?\r\n");
349 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
351 ret = SM_Block(state, data, GOP_GetMemoryStatus);
356 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
361 ret = SetCharset(state);
364 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
367 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
368 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
370 ret = SM_Block(state, data, GOP_ReadPhonebook);
375 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
378 return Initialise(data, state);
379 if ((op > GOP_Init) && (op < GOP_Max))
380 if (AT_Functions[op])
381 return (*AT_Functions[op])(data, state);
382 return GE_NOTIMPLEMENTED;
386 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
395 if (buf.line1 == NULL)
396 return GE_INVALIDPHBOOKLOCATION;
398 if (strncmp(buffer, "AT+CPBR", 7)) {
399 return GE_NONE; /*FIXME*/
402 if (!strncmp(buf.line2, "OK", 2)) {
403 if (data->PhonebookEntry) {
404 *(data->PhonebookEntry->Number) = '\0';
405 *(data->PhonebookEntry->Name) = '\0';
406 data->PhonebookEntry->Group = 0;
407 data->PhonebookEntry->SubEntriesCount = 0;
411 if (data->PhonebookEntry) {
412 data->PhonebookEntry->Group = 0;
413 data->PhonebookEntry->SubEntriesCount = 0;
414 pos = strchr(buf.line2, '\"');
417 endpos = strchr(++pos, '\"');
420 strcpy(data->PhonebookEntry->Number, pos);
424 pos = strchr(++endpos, '\"');
428 l = pos - (char *)buffer;
429 endpos = memchr(pos, '\"', length - l);
433 DecodeAscii(data->PhonebookEntry->Name, pos, l);
434 *(data->PhonebookEntry->Name + l) = '\0';
441 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
449 if (buf.line1 == NULL)
450 return GE_INVALIDMEMORYTYPE;
452 if (data->MemoryStatus) {
453 if (strstr(buf.line2,"+CPBS")) {
454 pos = strchr(buf.line2, ',');
456 data->MemoryStatus->Used = atoi(++pos);
458 data->MemoryStatus->Used = 100;
459 data->MemoryStatus->Free = 0;
462 pos = strchr(pos, ',');
464 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
474 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
482 if ((buf.line1 == NULL) || (buf.line2 == NULL))
485 if (!strncmp(buffer, "AT+CBC", 6)) {
486 if (data->BatteryLevel) {
487 *(data->BatteryUnits) = GBU_Percentage;
488 pos = strchr(buf.line2, ',');
491 *(data->BatteryLevel) = atoi(pos);
493 *(data->BatteryLevel) = 1;
496 if (data->PowerSource) {
497 *(data->PowerSource) = 0;
498 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
499 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
506 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
514 if (buf.line1 == NULL)
517 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
518 *(data->RFUnits) = GRF_CSQ;
519 pos1 = buf.line2 + 6;
520 pos2 = strchr(buf.line2, ',');
522 *(data->RFLevel) = atoi(pos1);
524 *(data->RFLevel) = 1;
531 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
538 if (buf.line1 == NULL)
539 return GE_NONE; /* Fixme */
540 if (!strncmp(buffer, "AT+CG", 5)) {
541 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
542 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
543 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
544 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
550 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
558 if (buf.line1 == NULL)
565 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
570 char manufacturer[20];
573 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
575 /* Copy in the phone info */
576 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
578 for (i=0; i<GOP_Max; i++) {
579 AT_Functions[i] = NULL;
580 IncomingFunctions[i].MessageType = 0;
581 IncomingFunctions[i].Functions = NULL;
583 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
584 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
585 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
588 switch (state->Link.ConnectionType) {
590 if (!strcmp(setupdata->Model, "dancall"))
591 CBUS_Initialise(state);
592 else if (!strcmp(setupdata->Model, "AT-HW"))
593 ATBUS_Initialise(state, true);
595 ATBUS_Initialise(state, false);
598 return GE_NOTSUPPORTED;
601 SM_Initialise(state);
603 SetEcho(&data, state);
605 GSM_DataClear(&data);
607 ret = state->Phone.Functions(GOP_GetModel, &data, state);
608 if (ret != GE_NONE) return ret;
609 GSM_DataClear(&data);
610 data.Manufacturer = manufacturer;
611 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
612 if (ret != GE_NONE) return ret;
614 if (!strncasecmp(manufacturer, "ericsson", 8))
615 AT_InitEricsson(state, model, setupdata->Model);
616 if (!strncasecmp(manufacturer, "siemens", 7))
617 AT_InitSiemens(state, model, setupdata->Model);
618 if (!strncasecmp(manufacturer, "nokia", 5))
619 AT_InitNokia(state, model, setupdata->Model);
625 void splitlines(AT_LineBuffer *buf)
629 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
633 pos = findcrlf(buf->line1, 0, buf->length);
636 buf->line2 = skipcrlf(++pos);
638 buf->line2 = buf->line1;
640 pos = findcrlf(buf->line2, 1, buf->length);
643 buf->line3 = skipcrlf(++pos);
645 buf->line3 = buf->line2;
651 * increments the argument until a char unequal to
652 * <cr> or <lf> is found. returns the new position.
655 char *skipcrlf(char *str)
659 while ((*str == '\n') || (*str == '\r'))
666 * searches for <cr> or <lf> and returns the first
667 * occurrence. if test is set, the gsm char @ which
668 * is 0x00 is not considered as end of string.
669 * return NULL if no <cr> or <lf> was found in the
670 * range of max bytes.
673 char *findcrlf(char *str, int test, int max)
677 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
681 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))