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.4 2002/04/03 00:08:08 short
18 Found in "gnokii-working" directory, some November-patches version
20 Revision 1.5 2001/11/08 16:49:19 pkot
23 Revision 1.4 2001/08/20 23:36:27 pkot
24 More cleanup in AT code (Manfred Jonsson)
26 Revision 1.3 2001/08/20 23:27:37 pkot
27 Add hardware shakehand to the link layer (Manfred Jonsson)
29 Revision 1.2 2001/08/09 11:51:39 pkot
30 Generic AT support updates and cleanup (Manfred Jonsson)
32 Revision 1.1 2001/07/27 00:02:21 pkot
33 Generic AT support for the new structure (Manfred Jonsson)
42 #include "gsm-common.h"
43 #include "gsm-statemachine.h"
44 #include "gsm-encoding.h"
45 #include "phones/generic.h"
46 #include "links/atbus.h"
47 #include "links/cbus.h"
50 #define ARRAY_LEN(x) (sizeof((x))/sizeof((x)[0]))
52 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
53 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
54 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
56 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
57 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
58 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
59 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
60 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
61 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
62 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
63 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
64 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
67 typedef GSM_Error (*AT_FunctionType)(GSM_Data *d, GSM_Statemachine *s);
71 } AT_FunctionInitType;
73 static AT_FunctionType AT_Functions[GOP_Max];
74 static AT_FunctionInitType AT_FunctionInit[] = {
75 { GOP_GetModel, AT_GetModel },
76 { GOP_GetRevision, AT_GetRevision },
77 { GOP_GetImei, AT_GetIMEI },
78 { GOP_GetManufacturer, AT_GetManufacturer },
79 { GOP_Identify, AT_Identify },
80 { GOP_GetBatteryLevel, AT_GetBattery },
81 { GOP_GetPowersource, AT_GetBattery },
82 { GOP_GetRFLevel, AT_GetRFLevel },
83 { GOP_GetMemoryStatus, AT_GetMemoryStatus },
84 { GOP_ReadPhonebook, AT_ReadPhonebook },
88 char *skipcrlf(char *str);
89 char *findcrlf(char *str, int test);
92 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
93 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
96 /* Mobile phone information */
98 static GSM_IncomingFunctionType IncomingFunctions[] = {
104 GSM_Phone phone_at = {
106 PGEN_IncomingDefault,
108 "AT|AT-HW|dancall", /* Supported models */
109 99, /* Max RF Level */
110 0, /* Min RF Level */
111 GRF_CSQ, /* RF level units */
112 100, /* Max Battery Level */
113 0, /* Min Battery Level */
114 GBU_Percentage, /* Battery level units */
115 0, /* Have date/time support */
116 0, /* Alarm supports time only */
117 0, /* Alarms available - FIXME */
118 0, 0, /* Startup logo size - FIXME */
119 0, 0, /* Op logo size */
120 0, 0 /* Caller logo size */
126 static GSM_MemoryType memorytype = GMT_XX;
127 static int atcharset = 0;
129 static char *memorynames[] = {
130 "ME", /* Internal memory of the mobile equipment */
131 "SM", /* SIM card memory */
132 "FD", /* Fixed dial numbers */
133 "ON", /* Own numbers */
134 "EN", /* Emergency numbers */
135 "DC", /* Dialled numbers */
136 "RC", /* Received numbers */
137 "MC", /* Missed numbers */
138 "LD", /* Last dialed */
139 "MT", /* combined ME and SIM phonebook */
140 "TA", /* for compatibility only: TA=computer memory */
141 "CB", /* Currently selected memory */
144 /* LinkOK is always true for now... */
145 bool ATGEN_LinkOK = true;
148 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
152 sprintf(req, "ATE1\r\n");
153 if (SM_SendMessage(state, 6, 1, req) != GE_NONE) return GE_NOTREADY;
154 return SM_Block(state, data, 1);
158 static GSM_Error SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
161 GSM_Error ret = GE_NONE;
164 if (mt != memorytype) {
165 sprintf(req, "AT+CPBS=%s\r\n", memorynames[mt]);
166 ret = SM_SendMessage(state, 12, 1, req);
169 GSM_DataClear(&data);
170 ret = SM_Block(state, &data, 1);
178 static GSM_Error SetCharset(GSM_Statemachine *state)
181 GSM_Error ret = GE_NONE;
184 if (atcharset == 0) {
185 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
186 ret = SM_SendMessage(state, 15, 1, req);
189 GSM_DataClear(&data);
190 ret = SM_Block(state, &data, 1);
198 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
202 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
204 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
206 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
208 return Functions(GOP_GetImei, data, state);
212 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
216 sprintf(req, "AT+CGMM\r\n");
217 if (SM_SendMessage(state, 9, 1, req) != GE_NONE) return GE_NOTREADY;
218 return SM_Block(state, data, 1);
222 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
226 sprintf(req, "AT+CGMI\r\n");
227 if (SM_SendMessage(state, 9, 1, req) != GE_NONE) return GE_NOTREADY;
228 return SM_Block(state, data, 1);
232 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
236 sprintf(req, "AT+CGMR\r\n");
237 if (SM_SendMessage(state, 9, 1, req) != GE_NONE) return GE_NOTREADY;
238 return SM_Block(state, data, 1);
242 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
246 sprintf(req, "AT+CGSN\r\n");
247 if (SM_SendMessage(state, 9, 1, req) != GE_NONE) return GE_NOTREADY;
248 return SM_Block(state, data, 1);
252 /* gets battery level and power source */
254 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
258 sprintf(req, "AT+CBC\r\n");
259 if (SM_SendMessage(state, 8, 1, req) != GE_NONE) return GE_NOTREADY;
260 return SM_Block(state, data, 1);
264 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
268 sprintf(req, "AT+CSQ\r\n");
269 if (SM_SendMessage(state, 8, 1, req) != GE_NONE) return GE_NOTREADY;
270 return SM_Block(state, data, 1);
274 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
279 ret = SetMemoryType(data->MemoryStatus->MemoryType, state);
282 sprintf(req, "AT+CPBS?\r\n");
283 if (SM_SendMessage(state, 10, 1, req) != GE_NONE)
285 ret = SM_Block(state, data, 1);
290 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
295 ret = SetCharset(state);
298 ret = SetMemoryType(data->PhonebookEntry->MemoryType, state);
301 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
302 if (SM_SendMessage(state, strlen(req), 1, req) != GE_NONE)
304 ret = SM_Block(state, data, 1);
309 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
312 return Initialise(data, state);
313 if ((op > GOP_Init) && (op < GOP_Max))
314 if (AT_Functions[op])
315 return (*AT_Functions[op])(data, state);
316 return GE_NOTIMPLEMENTED;
320 static GSM_Error ReplyReadPhonebook(GSM_Data *data, char *line, int error, int len)
326 return GE_INVALIDPHBOOKLOCATION;
328 if (!strncmp(line, "OK", 2)) {
329 if (data->PhonebookEntry) {
330 *(data->PhonebookEntry->Number) = '\0';
331 *(data->PhonebookEntry->Name) = '\0';
332 data->PhonebookEntry->Group = 0;
333 data->PhonebookEntry->SubEntriesCount = 0;
337 if (data->PhonebookEntry) {
338 data->PhonebookEntry->Group = 0;
339 data->PhonebookEntry->SubEntriesCount = 0;
340 pos = strchr(line, '\"');
343 endpos = strchr(++pos, '\"');
346 strcpy(data->PhonebookEntry->Number, pos);
350 pos = strchr(++endpos, '\"');
353 endpos = memchr(++pos, '\"', len);
356 DecodeAscii(data->PhonebookEntry->Name, pos, l);
357 *(data->PhonebookEntry->Name + l) = '\0';
364 static GSM_Error ReplyMemoryStatus(GSM_Data *data, char *line, int error)
369 return GE_INVALIDMEMORYTYPE;
370 if (data->MemoryStatus) {
371 pos = strchr(line, ',');
373 data->MemoryStatus->Used = atoi(++pos);
374 pos = strchr(pos, ',');
376 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
382 static GSM_Error ReplyBattery(GSM_Data *data, char *line, int error)
386 if (data->BatteryLevel) {
387 *(data->BatteryUnits) = GBU_Percentage;
388 pos = strchr(line, ',');
391 *(data->BatteryLevel) = atoi(pos);
393 *(data->BatteryLevel) = 1;
396 if (data->PowerSource) {
397 *(data->PowerSource) = 0;
398 if (*line == '1') *(data->PowerSource) = GPS_ACDC;
399 if (*line == '0') *(data->PowerSource) = GPS_BATTERY;
405 static GSM_Error ReplyRFLevel(GSM_Data *data, char *line, int error)
410 *(data->RFUnits) = GRF_CSQ;
412 buf = strchr(line, ',');
414 *(data->RFLevel) = atoi(pos);
416 *(data->RFLevel) = 1;
423 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
425 char *line2, *line3, *pos;
428 if ((length > 7) && (!strncmp(buffer+length-7, "ERROR", 5)))
430 pos = findcrlf(buffer, 0);
433 line2 = skipcrlf(++pos);
437 pos = findcrlf(line2, 1);
440 line3 = skipcrlf(++pos);
445 if (!strncmp(buffer, "AT+C", 4)) {
446 if (*(buffer+4) =='G') {
447 REPLY_SIMPLETEXT(buffer+5, line2, "SN", data->Imei);
448 REPLY_SIMPLETEXT(buffer+5, line2, "MM", data->Model);
449 REPLY_SIMPLETEXT(buffer+5, line2, "MI", data->Manufacturer);
450 REPLY_SIMPLETEXT(buffer+5, line2, "MR", data->Revision);
451 } else if (!strncmp(buffer+4, "SQ", 2)) {
452 ReplyRFLevel(data, line2, error);
453 } else if (!strncmp(buffer+4, "BC", 2)) {
454 ReplyBattery(data, line2, error);
455 } else if (!strncmp(buffer+4, "PB", 2)) {
456 if (*(buffer+6) == 'S') {
457 ReplyMemoryStatus(data, line2, error);
458 } else if (*(buffer+6) == 'R') {
459 ReplyReadPhonebook(data, line2, error, length);
467 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
472 char manufacturer[20];
475 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
477 /* Copy in the phone info */
478 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
480 for (i=0; i<GOP_Max; i++)
481 AT_Functions[i] = NULL;
482 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++)
483 AT_Functions[AT_FunctionInit[i].gop] = AT_FunctionInit[i].func;
485 switch (state->Link.ConnectionType) {
487 if (!strcmp(setupdata->Model, "dancall"))
488 CBUS_Initialise(state);
489 else if (!strcmp(setupdata->Model, "AT-HW"))
490 ATBUS_Initialise(state, true);
492 ATBUS_Initialise(state, false);
495 return GE_NOTSUPPORTED;
498 SM_Initialise(state);
500 SetEcho(&data, state);
502 GSM_DataClear(&data);
504 ret = state->Phone.Functions(GOP_GetModel, &data, state);
505 if (ret != GE_NONE) return ret;
506 GSM_DataClear(&data);
507 data.Model = manufacturer;
508 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
509 if (ret != GE_NONE) return ret;
512 if (!strcasecmp(manufacturer, "siemens"))
513 AT_InitSiemens(state, model, setupdata->Model, AT_Functions);
514 if (!strcasecmp(manufacturer, "ericsson"))
515 AT_InitEricsson(state, model, setupdata->Model, AT_Functions);
523 * increments the argument until a char unequal to
524 * <cr> or <lf> is found. returns the new position.
527 char *skipcrlf(char *str)
531 while ((*str == '\n') || (*str == '\r'))
538 * searches for <cr> or <lf> and returns the first
539 * occurrence. if test is set, the gsm char @ which
540 * is 0x00 is not considered as end of string.
541 * return NULL if test is not set and no <cr> or
543 * TODO should ask for maximum length.
546 char *findcrlf(char *str, int test)
550 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test))