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.6.1 2001/11/25 23:04:51 short
19 * g{,v}asprintf() - "asprintf()" compatibility emulation
20 * ARRAY_LEN() - sizeof(x)/sizeof(*x)
21 * SAFE_STRNCPY{,_SIZEOF}() - strncpy with variable-size autodetection
22 * G_GNUC_PRINTF - GCC attribute from glib
23 * N_(x) - missing in localization macros
25 Revision 1.1.1.1 2001/11/25 21:59:11 short
26 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
28 Revision 1.6 2001/11/19 13:03:18 pkot
31 Revision 1.5 2001/11/08 16:49:19 pkot
34 Revision 1.4 2001/08/20 23:36:27 pkot
35 More cleanup in AT code (Manfred Jonsson)
37 Revision 1.3 2001/08/20 23:27:37 pkot
38 Add hardware shakehand to the link layer (Manfred Jonsson)
40 Revision 1.2 2001/08/09 11:51:39 pkot
41 Generic AT support updates and cleanup (Manfred Jonsson)
43 Revision 1.1 2001/07/27 00:02:21 pkot
44 Generic AT support for the new structure (Manfred Jonsson)
53 #include "gsm-common.h"
54 #include "gsm-statemachine.h"
55 #include "gsm-encoding.h"
56 #include "phones/generic.h"
57 #include "phones/atgen.h"
58 #include "phones/ateric.h"
59 #include "phones/atsie.h"
60 #include "phones/atnok.h"
61 #include "links/atbus.h"
62 #include "links/cbus.h"
65 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
66 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
67 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
68 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
69 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
70 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
71 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
72 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
74 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
75 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
76 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
77 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
78 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
79 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
80 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
81 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
82 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
86 AT_SendFunctionType sfunc;
87 GSM_RecvFunctionType rfunc;
88 } AT_FunctionInitType;
91 /* Mobile phone information */
92 static AT_SendFunctionType AT_Functions[GOP_Max];
93 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
94 static AT_FunctionInitType AT_FunctionInit[] = {
95 { GOP_Init, NULL, Reply },
96 { GOP_GetModel, AT_GetModel, ReplyIdentify },
97 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
98 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
99 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
100 { GOP_Identify, AT_Identify, ReplyIdentify },
101 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
102 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
103 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
104 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
105 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
109 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
110 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
113 GSM_Phone phone_at = {
115 PGEN_IncomingDefault,
117 "AT|AT-HW|dancall", /* Supported models */
118 99, /* Max RF Level */
119 0, /* Min RF Level */
120 GRF_CSQ, /* RF level units */
121 100, /* Max Battery Level */
122 0, /* Min Battery Level */
123 GBU_Percentage, /* Battery level units */
124 0, /* Have date/time support */
125 0, /* Alarm supports time only */
126 0, /* Alarms available - FIXME */
127 0, 0, /* Startup logo size - FIXME */
128 0, 0, /* Op logo size */
129 0, 0 /* Caller logo size */
135 static GSM_MemoryType memorytype = GMT_XX;
136 static int atcharset = 0;
138 static char *memorynames[] = {
139 "ME", /* Internal memory of the mobile equipment */
140 "SM", /* SIM card memory */
141 "FD", /* Fixed dial numbers */
142 "ON", /* Own numbers */
143 "EN", /* Emergency numbers */
144 "DC", /* Dialled numbers */
145 "RC", /* Received numbers */
146 "MC", /* Missed numbers */
147 "LD", /* Last dialed */
148 "MT", /* combined ME and SIM phonebook */
149 "TA", /* for compatibility only: TA=computer memory */
150 "CB", /* Currently selected memory */
153 /* LinkOK is always true for now... */
154 bool ATGEN_LinkOK = true;
157 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
161 GSM_RecvFunctionType oldfunc;
163 if (type >= GOP_Max) {
164 return (GSM_RecvFunctionType) -1;
167 IncomingFunctions[pos].MessageType = type;
168 IncomingFunctions[pos].Functions = func;
172 for (i=0; i < pos; i++) {
173 if (IncomingFunctions[i].MessageType == type) {
174 oldfunc = IncomingFunctions[i].Functions;
175 IncomingFunctions[i].Functions = func;
179 if (pos < GOP_Max-1) {
180 IncomingFunctions[pos].MessageType = type;
181 IncomingFunctions[pos].Functions = func;
188 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
190 AT_SendFunctionType f;
192 f = AT_Functions[type];
193 AT_Functions[type] = func;
198 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
202 sprintf(req, "ATE1\r\n");
203 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
204 return SM_Block(state, data, GOP_Init);
208 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
211 GSM_Error ret = GE_NONE;
214 if (mt != memorytype) {
215 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
216 ret = SM_SendMessage(state, 14, GOP_Init, req);
219 GSM_DataClear(&data);
220 ret = SM_Block(state, &data, GOP_Init);
228 static GSM_Error SetCharset(GSM_Statemachine *state)
231 GSM_Error ret = GE_NONE;
234 if (atcharset == 0) {
235 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
236 ret = SM_SendMessage(state, 15, GOP_Init, req);
239 GSM_DataClear(&data);
240 ret = SM_Block(state, &data, GOP_Init);
248 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
252 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
254 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
256 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
258 return Functions(GOP_GetImei, data, state);
262 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
266 sprintf(req, "AT+CGMM\r\n");
267 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
269 return SM_Block(state, data, GOP_Identify);
273 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
277 sprintf(req, "AT+CGMI\r\n");
278 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
280 return SM_Block(state, data, GOP_Identify);
284 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
288 sprintf(req, "AT+CGMR\r\n");
289 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
291 return SM_Block(state, data, GOP_Identify);
295 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
299 sprintf(req, "AT+CGSN\r\n");
300 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
302 return SM_Block(state, data, GOP_Identify);
306 /* gets battery level and power source */
308 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
312 sprintf(req, "AT+CBC\r\n");
313 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
315 return SM_Block(state, data, GOP_GetBatteryLevel);
319 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
323 sprintf(req, "AT+CSQ\r\n");
324 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
326 return SM_Block(state, data, GOP_GetRFLevel);
330 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
335 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
338 sprintf(req, "AT+CPBS?\r\n");
339 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
341 ret = SM_Block(state, data, GOP_GetMemoryStatus);
342 if (ret != GE_UNKNOWN)
344 sprintf(req, "AT+CPBR=?\r\n");
345 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
347 ret = SM_Block(state, data, GOP_GetMemoryStatus);
352 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
357 ret = SetCharset(state);
360 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
363 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
364 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
366 ret = SM_Block(state, data, GOP_ReadPhonebook);
371 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
374 return Initialise(data, state);
375 if ((op > GOP_Init) && (op < GOP_Max))
376 if (AT_Functions[op])
377 return (*AT_Functions[op])(data, state);
378 return GE_NOTIMPLEMENTED;
382 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
391 if (buf.line1 == NULL)
392 return GE_INVALIDPHBOOKLOCATION;
394 if (strncmp(buffer, "AT+CPBR", 7)) {
395 return GE_NONE; /*FIXME*/
398 if (!strncmp(buf.line2, "OK", 2)) {
399 if (data->PhonebookEntry) {
400 *(data->PhonebookEntry->Number) = '\0';
401 *(data->PhonebookEntry->Name) = '\0';
402 data->PhonebookEntry->Group = 0;
403 data->PhonebookEntry->SubEntriesCount = 0;
407 if (data->PhonebookEntry) {
408 data->PhonebookEntry->Group = 0;
409 data->PhonebookEntry->SubEntriesCount = 0;
410 pos = strchr(buf.line2, '\"');
413 endpos = strchr(++pos, '\"');
416 strcpy(data->PhonebookEntry->Number, pos);
420 pos = strchr(++endpos, '\"');
424 l = pos - (char *)buffer;
425 endpos = memchr(pos, '\"', length - l);
429 DecodeAscii(data->PhonebookEntry->Name, pos, l);
430 *(data->PhonebookEntry->Name + l) = '\0';
437 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
445 if (buf.line1 == NULL)
446 return GE_INVALIDMEMORYTYPE;
448 if (data->MemoryStatus) {
449 if (strstr(buf.line2,"+CPBS")) {
450 pos = strchr(buf.line2, ',');
452 data->MemoryStatus->Used = atoi(++pos);
454 data->MemoryStatus->Used = 100;
455 data->MemoryStatus->Free = 0;
458 pos = strchr(pos, ',');
460 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
470 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
478 if ((buf.line1 == NULL) || (buf.line2 == NULL))
481 if (!strncmp(buffer, "AT+CBC", 6)) {
482 if (data->BatteryLevel) {
483 *(data->BatteryUnits) = GBU_Percentage;
484 pos = strchr(buf.line2, ',');
487 *(data->BatteryLevel) = atoi(pos);
489 *(data->BatteryLevel) = 1;
492 if (data->PowerSource) {
493 *(data->PowerSource) = 0;
494 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
495 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
502 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
510 if (buf.line1 == NULL)
513 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
514 *(data->RFUnits) = GRF_CSQ;
515 pos1 = buf.line2 + 6;
516 pos2 = strchr(buf.line2, ',');
518 *(data->RFLevel) = atoi(pos1);
520 *(data->RFLevel) = 1;
527 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
534 if (buf.line1 == NULL)
535 return GE_NONE; /* Fixme */
536 if (!strncmp(buffer, "AT+CG", 5)) {
537 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
538 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
539 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
540 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
546 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
554 if (buf.line1 == NULL)
561 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
566 char manufacturer[20];
569 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
571 /* Copy in the phone info */
572 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
574 for (i=0; i<GOP_Max; i++) {
575 AT_Functions[i] = NULL;
576 IncomingFunctions[i].MessageType = 0;
577 IncomingFunctions[i].Functions = NULL;
579 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
580 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
581 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
584 switch (state->Link.ConnectionType) {
586 if (!strcmp(setupdata->Model, "dancall"))
587 CBUS_Initialise(state);
588 else if (!strcmp(setupdata->Model, "AT-HW"))
589 ATBUS_Initialise(state, true);
591 ATBUS_Initialise(state, false);
594 return GE_NOTSUPPORTED;
597 SM_Initialise(state);
599 SetEcho(&data, state);
601 GSM_DataClear(&data);
603 ret = state->Phone.Functions(GOP_GetModel, &data, state);
604 if (ret != GE_NONE) return ret;
605 GSM_DataClear(&data);
606 data.Manufacturer = manufacturer;
607 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
608 if (ret != GE_NONE) return ret;
610 if (!strncasecmp(manufacturer, "ericsson", 8))
611 AT_InitEricsson(state, model, setupdata->Model);
612 if (!strncasecmp(manufacturer, "siemens", 7))
613 AT_InitSiemens(state, model, setupdata->Model);
614 if (!strncasecmp(manufacturer, "nokia", 5))
615 AT_InitNokia(state, model, setupdata->Model);
621 void splitlines(AT_LineBuffer *buf)
625 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
629 pos = findcrlf(buf->line1, 0, buf->length);
632 buf->line2 = skipcrlf(++pos);
634 buf->line2 = buf->line1;
636 pos = findcrlf(buf->line2, 1, buf->length);
639 buf->line3 = skipcrlf(++pos);
641 buf->line3 = buf->line2;
647 * increments the argument until a char unequal to
648 * <cr> or <lf> is found. returns the new position.
651 char *skipcrlf(char *str)
655 while ((*str == '\n') || (*str == '\r'))
662 * searches for <cr> or <lf> and returns the first
663 * occurrence. if test is set, the gsm char @ which
664 * is 0x00 is not considered as end of string.
665 * return NULL if no <cr> or <lf> was found in the
666 * range of max bytes.
669 char *findcrlf(char *str, int test, int max)
673 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
677 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))