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.8.1 2001/11/27 21:30:44 short
18 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
20 Revision 1.1.1.1.2.1 2001/11/27 04:37:59 short
21 Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
23 Revision 1.1.1.2 2001/11/27 04:19:30 short
24 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 05:17 CET 2001
26 Revision 1.7 2001/11/26 18:06:08 pkot
27 Checking for *printf functions, N_(x) for localization, generic ARRAY_LEN, SAFE_STRNCPY, G_GNUC_PRINTF (Jan Kratochvil)
29 Revision 1.6 2001/11/19 13:03:18 pkot
32 Revision 1.5 2001/11/08 16:49:19 pkot
35 Revision 1.4 2001/08/20 23:36:27 pkot
36 More cleanup in AT code (Manfred Jonsson)
38 Revision 1.3 2001/08/20 23:27:37 pkot
39 Add hardware shakehand to the link layer (Manfred Jonsson)
41 Revision 1.2 2001/08/09 11:51:39 pkot
42 Generic AT support updates and cleanup (Manfred Jonsson)
44 Revision 1.1 2001/07/27 00:02:21 pkot
45 Generic AT support for the new structure (Manfred Jonsson)
54 #include "gsm-common.h"
55 #include "gsm-statemachine.h"
56 #include "gsm-encoding.h"
57 #include "phones/generic.h"
58 #include "phones/atgen.h"
59 #include "phones/ateric.h"
60 #include "phones/atsie.h"
61 #include "phones/atnok.h"
62 #include "links/atbus.h"
63 #include "links/cbus.h"
66 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
67 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
68 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
69 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
70 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
71 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
72 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
73 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
75 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
76 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
77 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
78 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
79 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
80 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
81 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
82 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
83 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
87 AT_SendFunctionType sfunc;
88 GSM_RecvFunctionType rfunc;
89 } AT_FunctionInitType;
92 /* Mobile phone information */
93 static AT_SendFunctionType AT_Functions[GOP_Max];
94 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
95 static AT_FunctionInitType AT_FunctionInit[] = {
96 { GOP_Init, NULL, Reply },
97 { GOP_GetModel, AT_GetModel, ReplyIdentify },
98 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
99 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
100 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
101 { GOP_Identify, AT_Identify, ReplyIdentify },
102 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
103 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
104 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
105 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
106 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
110 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
111 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
114 GSM_Phone phone_at = {
116 PGEN_IncomingDefault,
118 "AT|AT-HW|dancall", /* Supported models */
119 99, /* Max RF Level */
120 0, /* Min RF Level */
121 GRF_CSQ, /* RF level units */
122 100, /* Max Battery Level */
123 0, /* Min Battery Level */
124 GBU_Percentage, /* Battery level units */
125 0, /* Have date/time support */
126 0, /* Alarm supports time only */
127 0, /* Alarms available - FIXME */
128 0, 0, /* Startup logo size - FIXME */
129 0, 0, /* Op logo size */
130 0, 0 /* Caller logo size */
136 static GSM_MemoryType memorytype = GMT_XX;
137 static int atcharset = 0;
139 static char *memorynames[] = {
140 "ME", /* Internal memory of the mobile equipment */
141 "SM", /* SIM card memory */
142 "FD", /* Fixed dial numbers */
143 "ON", /* Own numbers */
144 "EN", /* Emergency numbers */
145 "DC", /* Dialled numbers */
146 "RC", /* Received numbers */
147 "MC", /* Missed numbers */
148 "LD", /* Last dialed */
149 "MT", /* combined ME and SIM phonebook */
150 "TA", /* for compatibility only: TA=computer memory */
151 "CB", /* Currently selected memory */
154 /* LinkOK is always true for now... */
155 bool ATGEN_LinkOK = true;
158 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
162 GSM_RecvFunctionType oldfunc;
164 if (type >= GOP_Max) {
165 return (GSM_RecvFunctionType) -1;
168 IncomingFunctions[pos].MessageType = type;
169 IncomingFunctions[pos].Functions = func;
173 for (i=0; i < pos; i++) {
174 if (IncomingFunctions[i].MessageType == type) {
175 oldfunc = IncomingFunctions[i].Functions;
176 IncomingFunctions[i].Functions = func;
180 if (pos < GOP_Max-1) {
181 IncomingFunctions[pos].MessageType = type;
182 IncomingFunctions[pos].Functions = func;
189 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
191 AT_SendFunctionType f;
193 f = AT_Functions[type];
194 AT_Functions[type] = func;
199 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
203 sprintf(req, "ATE1\r\n");
204 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
205 return SM_Block(state, data, GOP_Init);
209 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
212 GSM_Error ret = GE_NONE;
215 if (mt != memorytype) {
216 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
217 ret = SM_SendMessage(state, 14, GOP_Init, req);
220 GSM_DataClear(&data);
221 ret = SM_Block(state, &data, GOP_Init);
229 static GSM_Error SetCharset(GSM_Statemachine *state)
232 GSM_Error ret = GE_NONE;
235 if (atcharset == 0) {
236 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
237 ret = SM_SendMessage(state, 15, GOP_Init, req);
240 GSM_DataClear(&data);
241 ret = SM_Block(state, &data, GOP_Init);
249 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
253 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
255 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
257 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
259 return Functions(GOP_GetImei, data, state);
263 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
267 sprintf(req, "AT+CGMM\r\n");
268 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
270 return SM_Block(state, data, GOP_Identify);
274 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
278 sprintf(req, "AT+CGMI\r\n");
279 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
281 return SM_Block(state, data, GOP_Identify);
285 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
289 sprintf(req, "AT+CGMR\r\n");
290 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
292 return SM_Block(state, data, GOP_Identify);
296 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
300 sprintf(req, "AT+CGSN\r\n");
301 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
303 return SM_Block(state, data, GOP_Identify);
307 /* gets battery level and power source */
309 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
313 sprintf(req, "AT+CBC\r\n");
314 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
316 return SM_Block(state, data, GOP_GetBatteryLevel);
320 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
324 sprintf(req, "AT+CSQ\r\n");
325 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
327 return SM_Block(state, data, GOP_GetRFLevel);
331 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
336 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
339 sprintf(req, "AT+CPBS?\r\n");
340 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
342 ret = SM_Block(state, data, GOP_GetMemoryStatus);
343 if (ret != GE_UNKNOWN)
345 sprintf(req, "AT+CPBR=?\r\n");
346 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
348 ret = SM_Block(state, data, GOP_GetMemoryStatus);
353 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
358 ret = SetCharset(state);
361 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
364 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
365 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
367 ret = SM_Block(state, data, GOP_ReadPhonebook);
372 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
375 return Initialise(data, state);
376 if ((op > GOP_Init) && (op < GOP_Max))
377 if (AT_Functions[op])
378 return (*AT_Functions[op])(data, state);
379 return GE_NOTIMPLEMENTED;
383 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
392 if (buf.line1 == NULL)
393 return GE_INVALIDPHBOOKLOCATION;
395 if (strncmp(buffer, "AT+CPBR", 7)) {
396 return GE_NONE; /*FIXME*/
399 if (!strncmp(buf.line2, "OK", 2)) {
400 if (data->PhonebookEntry) {
401 *(data->PhonebookEntry->Number) = '\0';
402 *(data->PhonebookEntry->Name) = '\0';
403 data->PhonebookEntry->Group = 0;
404 data->PhonebookEntry->SubEntriesCount = 0;
408 if (data->PhonebookEntry) {
409 data->PhonebookEntry->Group = 0;
410 data->PhonebookEntry->SubEntriesCount = 0;
411 pos = strchr(buf.line2, '\"');
414 endpos = strchr(++pos, '\"');
417 strcpy(data->PhonebookEntry->Number, pos);
421 pos = strchr(++endpos, '\"');
425 l = pos - (char *)buffer;
426 endpos = memchr(pos, '\"', length - l);
430 DecodeAscii(data->PhonebookEntry->Name, pos, l);
431 *(data->PhonebookEntry->Name + l) = '\0';
438 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
446 if (buf.line1 == NULL)
447 return GE_INVALIDMEMORYTYPE;
449 if (data->MemoryStatus) {
450 if (strstr(buf.line2,"+CPBS")) {
451 pos = strchr(buf.line2, ',');
453 data->MemoryStatus->Used = atoi(++pos);
455 data->MemoryStatus->Used = 100;
456 data->MemoryStatus->Free = 0;
459 pos = strchr(pos, ',');
461 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
471 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
479 if ((buf.line1 == NULL) || (buf.line2 == NULL))
482 if (!strncmp(buffer, "AT+CBC", 6)) {
483 if (data->BatteryLevel) {
484 *(data->BatteryUnits) = GBU_Percentage;
485 pos = strchr(buf.line2, ',');
488 *(data->BatteryLevel) = atoi(pos);
490 *(data->BatteryLevel) = 1;
493 if (data->PowerSource) {
494 *(data->PowerSource) = 0;
495 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
496 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
503 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
511 if (buf.line1 == NULL)
514 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
515 *(data->RFUnits) = GRF_CSQ;
516 pos1 = buf.line2 + 6;
517 pos2 = strchr(buf.line2, ',');
519 *(data->RFLevel) = atoi(pos1);
521 *(data->RFLevel) = 1;
528 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
535 if (buf.line1 == NULL)
536 return GE_NONE; /* Fixme */
537 if (!strncmp(buffer, "AT+CG", 5)) {
538 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
539 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
540 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
541 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
547 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
555 if (buf.line1 == NULL)
562 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
567 char manufacturer[20];
570 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
572 /* Copy in the phone info */
573 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
575 for (i=0; i<GOP_Max; i++) {
576 AT_Functions[i] = NULL;
577 IncomingFunctions[i].MessageType = 0;
578 IncomingFunctions[i].Functions = NULL;
580 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
581 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
582 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
585 switch (state->Link.ConnectionType) {
587 if (!strcmp(setupdata->Model, "dancall"))
588 CBUS_Initialise(state);
589 else if (!strcmp(setupdata->Model, "AT-HW"))
590 ATBUS_Initialise(state, true);
592 ATBUS_Initialise(state, false);
595 return GE_NOTSUPPORTED;
598 SM_Initialise(state);
600 SetEcho(&data, state);
602 GSM_DataClear(&data);
604 ret = state->Phone.Functions(GOP_GetModel, &data, state);
605 if (ret != GE_NONE) return ret;
606 GSM_DataClear(&data);
607 data.Manufacturer = manufacturer;
608 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
609 if (ret != GE_NONE) return ret;
611 if (!strncasecmp(manufacturer, "ericsson", 8))
612 AT_InitEricsson(state, model, setupdata->Model);
613 if (!strncasecmp(manufacturer, "siemens", 7))
614 AT_InitSiemens(state, model, setupdata->Model);
615 if (!strncasecmp(manufacturer, "nokia", 5))
616 AT_InitNokia(state, model, setupdata->Model);
622 void splitlines(AT_LineBuffer *buf)
626 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
630 pos = findcrlf(buf->line1, 0, buf->length);
633 buf->line2 = skipcrlf(++pos);
635 buf->line2 = buf->line1;
637 pos = findcrlf(buf->line2, 1, buf->length);
640 buf->line3 = skipcrlf(++pos);
642 buf->line3 = buf->line2;
648 * increments the argument until a char unequal to
649 * <cr> or <lf> is found. returns the new position.
652 char *skipcrlf(char *str)
656 while ((*str == '\n') || (*str == '\r'))
663 * searches for <cr> or <lf> and returns the first
664 * occurrence. if test is set, the gsm char @ which
665 * is 0x00 is not considered as end of string.
666 * return NULL if no <cr> or <lf> was found in the
667 * range of max bytes.
670 char *findcrlf(char *str, int test, int max)
674 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
678 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))