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.
23 #include "gsm-common.h"
24 #include "gsm-statemachine.h"
25 #include "gsm-encoding.h"
26 #include "phones/generic.h"
27 #include "phones/atgen.h"
28 #include "phones/ateric.h"
29 #include "phones/atsie.h"
30 #include "phones/atnok.h"
31 #include "links/atbus.h"
32 #include "links/cbus.h"
35 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
36 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
37 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
38 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
39 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
40 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
41 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
42 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
43 static GSM_Error ReplyCallDivert(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
45 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
46 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
47 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
48 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
49 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
50 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state);
51 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state);
52 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state);
53 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state);
54 static GSM_Error AT_CallDivert(GSM_Data *data, GSM_Statemachine *state);
58 AT_SendFunctionType sfunc;
59 GSM_RecvFunctionType rfunc;
60 } AT_FunctionInitType;
63 /* Mobile phone information */
64 static AT_SendFunctionType AT_Functions[GOP_Max];
65 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
66 static AT_FunctionInitType AT_FunctionInit[] = {
67 { GOP_Init, NULL, Reply },
68 { GOP_GetModel, AT_GetModel, ReplyIdentify },
69 { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
70 { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
71 { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
72 { GOP_Identify, AT_Identify, ReplyIdentify },
73 { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
74 { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
75 { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
76 { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
77 { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
78 { GOP_CallDivert, AT_CallDivert, ReplyCallDivert }
82 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
83 if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
86 GSM_Phone phone_at = {
90 "AT|AT-HW|dancall", /* Supported models */
91 99, /* Max RF Level */
93 GRF_CSQ, /* RF level units */
94 100, /* Max Battery Level */
95 0, /* Min Battery Level */
96 GBU_Percentage, /* Battery level units */
97 0, /* Have date/time support */
98 0, /* Alarm supports time only */
99 0, /* Alarms available - FIXME */
100 0, 0, /* Startup logo size - FIXME */
101 0, 0, /* Op logo size */
102 0, 0 /* Caller logo size */
108 static GSM_MemoryType memorytype = GMT_XX;
109 static int atcharset = 0;
111 static char *memorynames[] = {
112 "ME", /* Internal memory of the mobile equipment */
113 "SM", /* SIM card memory */
114 "FD", /* Fixed dial numbers */
115 "ON", /* Own numbers */
116 "EN", /* Emergency numbers */
117 "DC", /* Dialled numbers */
118 "RC", /* Received numbers */
119 "MC", /* Missed numbers */
120 "LD", /* Last dialed */
121 "MT", /* combined ME and SIM phonebook */
122 "TA", /* for compatibility only: TA=computer memory */
123 "CB", /* Currently selected memory */
126 /* LinkOK is always true for now... */
127 bool ATGEN_LinkOK = true;
130 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
134 GSM_RecvFunctionType oldfunc;
136 if (type >= GOP_Max) {
137 return (GSM_RecvFunctionType) -1;
140 IncomingFunctions[pos].MessageType = type;
141 IncomingFunctions[pos].Functions = func;
145 for (i=0; i < pos; i++) {
146 if (IncomingFunctions[i].MessageType == type) {
147 oldfunc = IncomingFunctions[i].Functions;
148 IncomingFunctions[i].Functions = func;
152 if (pos < GOP_Max-1) {
153 IncomingFunctions[pos].MessageType = type;
154 IncomingFunctions[pos].Functions = func;
161 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
163 AT_SendFunctionType f;
165 f = AT_Functions[type];
166 AT_Functions[type] = func;
171 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
175 sprintf(req, "ATE1\r\n");
176 if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
177 return SM_Block(state, data, GOP_Init);
181 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
184 GSM_Error ret = GE_NONE;
187 if (mt != memorytype) {
188 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
189 ret = SM_SendMessage(state, 14, GOP_Init, req);
192 GSM_DataClear(&data);
193 ret = SM_Block(state, &data, GOP_Init);
201 static GSM_Error SetCharset(GSM_Statemachine *state)
204 GSM_Error ret = GE_NONE;
207 if (atcharset == 0) {
208 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
209 ret = SM_SendMessage(state, 15, GOP_Init, req);
212 GSM_DataClear(&data);
213 ret = SM_Block(state, &data, GOP_Init);
221 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
225 if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
227 if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
229 if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
231 return Functions(GOP_GetImei, data, state);
235 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
239 sprintf(req, "AT+CGMM\r\n");
240 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
242 return SM_Block(state, data, GOP_Identify);
246 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
250 sprintf(req, "AT+CGMI\r\n");
251 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
253 return SM_Block(state, data, GOP_Identify);
257 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
261 sprintf(req, "AT+CGMR\r\n");
262 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
264 return SM_Block(state, data, GOP_Identify);
268 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
272 sprintf(req, "AT+CGSN\r\n");
273 if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
275 return SM_Block(state, data, GOP_Identify);
279 /* gets battery level and power source */
281 static GSM_Error AT_GetBattery(GSM_Data *data, GSM_Statemachine *state)
285 sprintf(req, "AT+CBC\r\n");
286 if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
288 return SM_Block(state, data, GOP_GetBatteryLevel);
292 static GSM_Error AT_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
296 sprintf(req, "AT+CSQ\r\n");
297 if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
299 return SM_Block(state, data, GOP_GetRFLevel);
303 static GSM_Error AT_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
308 ret = AT_SetMemoryType(data->MemoryStatus->MemoryType, state);
311 sprintf(req, "AT+CPBS?\r\n");
312 if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
314 ret = SM_Block(state, data, GOP_GetMemoryStatus);
315 if (ret != GE_UNKNOWN)
317 sprintf(req, "AT+CPBR=?\r\n");
318 if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
320 ret = SM_Block(state, data, GOP_GetMemoryStatus);
325 static GSM_Error AT_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
330 ret = SetCharset(state);
333 ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType, state);
336 sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
337 if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
339 ret = SM_Block(state, data, GOP_ReadPhonebook);
344 static GSM_Error AT_CallDivert(GSM_Data *data, GSM_Statemachine *state)
348 if (!data->CallDivert) return GE_UNKNOWN;
350 sprintf(req, "AT+CCFC=");
352 switch (data->CallDivert->DType) {
353 case GSM_CDV_AllTypes:
359 case GSM_CDV_NoAnswer:
362 case GSM_CDV_OutOfReach:
366 dprintf("3. %d\n", data->CallDivert->DType);
367 return GE_NOTIMPLEMENTED;
369 if (data->CallDivert->Operation == GSM_CDV_Register)
370 sprintf(req, "%s,%d,\"%s\",%d,,,%d", req,
371 data->CallDivert->Operation,
372 data->CallDivert->Number.number,
373 data->CallDivert->Number.type,
374 data->CallDivert->Timeout);
376 sprintf(req, "%s,%d", req, data->CallDivert->Operation);
381 if (SM_SendMessage(state, strlen(req), GOP_CallDivert, req) != GE_NONE)
383 return SM_WaitFor(state, data, GOP_CallDivert);
386 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
389 return Initialise(data, state);
390 if ((op > GOP_Init) && (op < GOP_Max))
391 if (AT_Functions[op])
392 return (*AT_Functions[op])(data, state);
393 return GE_NOTIMPLEMENTED;
397 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
406 if (buf.line1 == NULL)
407 return GE_INVALIDPHBOOKLOCATION;
409 if (strncmp(buffer, "AT+CPBR", 7)) {
410 return GE_NONE; /*FIXME*/
413 if (!strncmp(buf.line2, "OK", 2)) {
414 if (data->PhonebookEntry) {
415 *(data->PhonebookEntry->Number) = '\0';
416 *(data->PhonebookEntry->Name) = '\0';
417 data->PhonebookEntry->Group = 0;
418 data->PhonebookEntry->SubEntriesCount = 0;
422 if (data->PhonebookEntry) {
423 data->PhonebookEntry->Group = 0;
424 data->PhonebookEntry->SubEntriesCount = 0;
425 pos = strchr(buf.line2, '\"');
428 endpos = strchr(++pos, '\"');
431 strcpy(data->PhonebookEntry->Number, pos);
435 pos = strchr(++endpos, '\"');
439 l = pos - (char *)buffer;
440 endpos = memchr(pos, '\"', length - l);
444 DecodeAscii(data->PhonebookEntry->Name, pos, l);
445 *(data->PhonebookEntry->Name + l) = '\0';
452 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
460 if (buf.line1 == NULL)
461 return GE_INVALIDMEMORYTYPE;
463 if (data->MemoryStatus) {
464 if (strstr(buf.line2,"+CPBS")) {
465 pos = strchr(buf.line2, ',');
467 data->MemoryStatus->Used = atoi(++pos);
469 data->MemoryStatus->Used = 100;
470 data->MemoryStatus->Free = 0;
473 pos = strchr(pos, ',');
475 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
485 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
493 if ((buf.line1 == NULL) || (buf.line2 == NULL))
496 if (!strncmp(buffer, "AT+CBC", 6)) {
497 if (data->BatteryLevel) {
498 *(data->BatteryUnits) = GBU_Percentage;
499 pos = strchr(buf.line2, ',');
502 *(data->BatteryLevel) = atoi(pos);
504 *(data->BatteryLevel) = 1;
507 if (data->PowerSource) {
508 *(data->PowerSource) = 0;
509 if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
510 if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
517 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
525 if (buf.line1 == NULL)
528 if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
529 *(data->RFUnits) = GRF_CSQ;
530 pos1 = buf.line2 + 6;
531 pos2 = strchr(buf.line2, ',');
533 *(data->RFLevel) = atoi(pos1);
535 *(data->RFLevel) = 1;
542 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
549 if (buf.line1 == NULL)
550 return GE_NONE; /* Fixme */
551 if (!strncmp(buffer, "AT+CG", 5)) {
552 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
553 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
554 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
555 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
560 static GSM_Error ReplyCallDivert(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
563 for (i = 0; i < length; i++) {
564 dprintf("%02x ", buffer[i]);
570 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
578 if (buf.line1 == NULL)
585 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
590 char manufacturer[20];
593 fprintf(stderr, "Initializing AT capable mobile phone ...\n");
595 /* Copy in the phone info */
596 memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
598 for (i=0; i<GOP_Max; i++) {
599 AT_Functions[i] = NULL;
600 IncomingFunctions[i].MessageType = 0;
601 IncomingFunctions[i].Functions = NULL;
603 for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
604 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
605 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
608 switch (state->Link.ConnectionType) {
610 if (!strcmp(setupdata->Model, "dancall"))
611 CBUS_Initialise(state);
612 else if (!strcmp(setupdata->Model, "AT-HW"))
613 ATBUS_Initialise(state, true);
615 ATBUS_Initialise(state, false);
618 return GE_NOTSUPPORTED;
621 SM_Initialise(state);
623 SetEcho(&data, state);
625 GSM_DataClear(&data);
627 ret = state->Phone.Functions(GOP_GetModel, &data, state);
628 if (ret != GE_NONE) return ret;
629 GSM_DataClear(&data);
630 data.Manufacturer = manufacturer;
631 ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
632 if (ret != GE_NONE) return ret;
634 if (!strncasecmp(manufacturer, "ericsson", 8))
635 AT_InitEricsson(state, model, setupdata->Model);
636 if (!strncasecmp(manufacturer, "siemens", 7))
637 AT_InitSiemens(state, model, setupdata->Model);
638 if (!strncasecmp(manufacturer, "nokia", 5))
639 AT_InitNokia(state, model, setupdata->Model);
645 void splitlines(AT_LineBuffer *buf)
649 if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
653 pos = findcrlf(buf->line1, 0, buf->length);
656 buf->line2 = skipcrlf(++pos);
658 buf->line2 = buf->line1;
660 pos = findcrlf(buf->line2, 1, buf->length);
663 buf->line3 = skipcrlf(++pos);
665 buf->line3 = buf->line2;
671 * increments the argument until a char unequal to
672 * <cr> or <lf> is found. returns the new position.
675 char *skipcrlf(char *str)
679 while ((*str == '\n') || (*str == '\r'))
686 * searches for <cr> or <lf> and returns the first
687 * occurrence. if test is set, the gsm char @ which
688 * is 0x00 is not considered as end of string.
689 * return NULL if no <cr> or <lf> was found in the
690 * range of max bytes.
693 char *findcrlf(char *str, int test, int max)
697 while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
701 if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))