This commit was generated by cvs2svn to compensate for changes in r158,
[gnokii.git] / common / phones / atgen.c
1 /*
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for mobile phones.
8
9   Copyright 2001 Manfred Jonsson <manfred.jonsson@gmx.de>
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12
13   This file provides functions specific to generic at command compatible
14   phones. See README for more details on supported mobile phones.
15
16   $Log$
17   Revision 1.1.1.4  2002/04/03 00:08:08  short
18   Found in "gnokii-working" directory, some November-patches version
19
20   Revision 1.5  2001/11/08 16:49:19  pkot
21   Cleanups
22
23   Revision 1.4  2001/08/20 23:36:27  pkot
24   More cleanup in AT code (Manfred Jonsson)
25
26   Revision 1.3  2001/08/20 23:27:37  pkot
27   Add hardware shakehand to the link layer (Manfred Jonsson)
28
29   Revision 1.2  2001/08/09 11:51:39  pkot
30   Generic AT support updates and cleanup (Manfred Jonsson)
31
32   Revision 1.1  2001/07/27 00:02:21  pkot
33   Generic AT support for the new structure (Manfred Jonsson)
34
35 */
36
37 #include <string.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40
41 #include "misc.h"
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"
48
49
50 #define ARRAY_LEN(x) (sizeof((x))/sizeof((x)[0]))
51
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);
55
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);
65
66
67 typedef GSM_Error (*AT_FunctionType)(GSM_Data *d, GSM_Statemachine *s);
68 typedef struct {
69         int gop;
70         AT_FunctionType func;
71 } AT_FunctionInitType;
72
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 },
85 };
86
87
88 char *skipcrlf(char *str);
89 char *findcrlf(char *str, int test);
90
91
92 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
93         if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
94
95
96 /* Mobile phone information */
97
98 static GSM_IncomingFunctionType IncomingFunctions[] = {
99         { 1, Reply },
100         { 0, NULL }
101 };
102
103
104 GSM_Phone phone_at = {
105         IncomingFunctions,
106         PGEN_IncomingDefault,
107         {
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 */
121         },
122         Functions
123 };
124
125
126 static GSM_MemoryType memorytype = GMT_XX;
127 static int atcharset = 0;
128
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 */
142 };
143
144 /* LinkOK is always true for now... */
145 bool ATGEN_LinkOK = true;
146
147
148 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
149 {
150         char req[128];
151
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);
155 }
156
157
158 static GSM_Error SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
159 {
160         char req[128];
161         GSM_Error ret = GE_NONE;
162         GSM_Data data;
163
164         if (mt != memorytype) {
165                 sprintf(req, "AT+CPBS=%s\r\n", memorynames[mt]);
166                 ret = SM_SendMessage(state, 12, 1, req);
167                 if (ret != GE_NONE)
168                         return GE_NOTREADY;
169                 GSM_DataClear(&data);
170                 ret = SM_Block(state, &data, 1);
171                 if (ret == GE_NONE)
172                         memorytype = mt;
173         }
174         return ret;
175 }
176
177
178 static GSM_Error SetCharset(GSM_Statemachine *state)
179 {
180         char req[128];
181         GSM_Error ret = GE_NONE;
182         GSM_Data data;
183
184         if (atcharset == 0) {
185                 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
186                 ret = SM_SendMessage(state, 15, 1, req);
187                 if (ret != GE_NONE)
188                         return GE_NOTREADY;
189                 GSM_DataClear(&data);
190                 ret = SM_Block(state, &data, 1);
191                 if (ret == GE_NONE)
192                         atcharset = 1;
193         }
194         return ret;
195 }
196
197
198 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
199 {
200         GSM_Error ret;
201
202         if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
203                 return ret;
204         if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
205                 return ret;
206         if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
207                 return ret;
208         return Functions(GOP_GetImei, data, state);
209 }
210
211
212 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
213 {
214         char req[128];
215
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);
219 }
220
221
222 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
223 {
224         char req[128];
225
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);
229 }
230
231
232 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
233 {
234         char req[128];
235
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);
239 }
240
241
242 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
243 {
244         char req[128];
245
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);
249 }
250
251
252 /* gets battery level and power source */
253
254 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state)
255 {
256         char req[128];
257
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);
261 }
262
263
264 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state)
265 {
266         char req[128];
267  
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);
271 }
272  
273  
274 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state)
275 {
276         char req[128];
277         GSM_Error ret;
278
279         ret = SetMemoryType(data->MemoryStatus->MemoryType,  state);
280         if (ret != GE_NONE)
281                 return ret;
282         sprintf(req, "AT+CPBS?\r\n");
283         if (SM_SendMessage(state, 10, 1, req) != GE_NONE)
284                 return GE_NOTREADY;
285         ret = SM_Block(state, data, 1);
286         return ret;
287 }
288
289
290 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state)
291 {
292         char req[128];
293         GSM_Error ret;
294
295         ret = SetCharset(state);
296         if (ret != GE_NONE)
297                 return ret;
298         ret = SetMemoryType(data->PhonebookEntry->MemoryType,  state);
299         if (ret != GE_NONE)
300                 return ret;
301         sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
302         if (SM_SendMessage(state, strlen(req), 1, req) != GE_NONE)
303                 return GE_NOTREADY;
304         ret = SM_Block(state, data, 1);
305         return ret;
306 }
307
308
309 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
310 {
311         if (op == GOP_Init)
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;
317 }
318
319
320 static GSM_Error ReplyReadPhonebook(GSM_Data *data, char *line, int error, int len)
321 {
322         char *pos, *endpos;
323         int l;
324
325         if (error) {
326                 return GE_INVALIDPHBOOKLOCATION;
327         }
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;
334                 }
335                 return GE_NONE;
336         }
337         if (data->PhonebookEntry) {
338                 data->PhonebookEntry->Group = 0;
339                 data->PhonebookEntry->SubEntriesCount = 0;
340                 pos = strchr(line, '\"');
341                 endpos = NULL;
342                 if (pos)
343                         endpos = strchr(++pos, '\"');
344                 if (endpos) {
345                         *endpos = '\0';
346                         strcpy(data->PhonebookEntry->Number, pos);
347                 }
348                 pos = NULL;
349                 if (endpos)
350                         pos = strchr(++endpos, '\"');
351                 endpos = NULL;
352                 if (pos)
353                         endpos = memchr(++pos, '\"', len);
354                 if (endpos) {
355                         l= endpos - pos;
356                         DecodeAscii(data->PhonebookEntry->Name, pos, l);
357                         *(data->PhonebookEntry->Name + l) = '\0';
358                 }
359         }
360         return GE_NONE;
361 }
362
363
364 static GSM_Error ReplyMemoryStatus(GSM_Data *data, char *line, int error)
365 {
366         char *pos;
367
368         if (error)
369                 return GE_INVALIDMEMORYTYPE;
370         if (data->MemoryStatus) {
371                 pos = strchr(line, ',');
372                 if (pos)
373                         data->MemoryStatus->Used = atoi(++pos);
374                 pos = strchr(pos, ',');
375                 if (pos)
376                         data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
377         }
378         return GE_NONE;
379 }
380
381
382 static GSM_Error ReplyBattery(GSM_Data *data, char *line, int error)
383 {
384         char *pos;
385
386         if (data->BatteryLevel) {
387                 *(data->BatteryUnits) = GBU_Percentage;
388                 pos = strchr(line, ',');
389                 if (pos) {
390                         pos++;
391                         *(data->BatteryLevel) = atoi(pos);
392                 } else {
393                         *(data->BatteryLevel) = 1;
394                 }
395         }
396         if (data->PowerSource) {
397                 *(data->PowerSource) = 0;
398                 if (*line == '1') *(data->PowerSource) = GPS_ACDC;
399                 if (*line == '0') *(data->PowerSource) = GPS_BATTERY;
400         }
401         return GE_NONE;
402 }
403
404
405 static GSM_Error ReplyRFLevel(GSM_Data *data, char *line, int error)
406 {
407         char *pos, *buf;
408
409         if (data->RFUnits) {
410                 *(data->RFUnits) = GRF_CSQ;
411                 pos = line + 6;
412                 buf = strchr(line, ',');
413                 if (pos < buf) {
414                         *(data->RFLevel) = atoi(pos);
415                 } else {
416                         *(data->RFLevel) = 1;
417                 }
418         }
419         return GE_NONE;
420 }
421
422
423 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
424 {
425         char *line2, *line3, *pos;
426         int error = 0;
427
428         if ((length > 7) && (!strncmp(buffer+length-7, "ERROR", 5)))
429                 error = 1;
430         pos = findcrlf(buffer, 0);
431         if (pos) {
432                 *pos = 0;
433                 line2 = skipcrlf(++pos);
434         } else {
435                 line2 = buffer;
436         }
437         pos = findcrlf(line2, 1);
438         if (pos) {
439                 *pos = 0;
440                 line3 = skipcrlf(++pos);
441         } else {
442                 line3 = line2;
443         }
444
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);
460                         }
461                 }
462         }
463         return GE_NONE;
464 }
465
466
467 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
468 {
469         GSM_Data data;
470         GSM_Error ret;
471         char model[20];
472         char manufacturer[20];
473         int i;
474
475         fprintf(stderr, "Initializing AT capable mobile phone ...\n");
476
477         /* Copy in the phone info */
478         memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
479
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;
484
485         switch (state->Link.ConnectionType) {
486         case GCT_Serial:
487                 if (!strcmp(setupdata->Model, "dancall"))
488                         CBUS_Initialise(state);
489                 else if (!strcmp(setupdata->Model, "AT-HW"))
490                         ATBUS_Initialise(state, true);
491                 else
492                         ATBUS_Initialise(state, false);
493                 break;
494         default:
495                 return GE_NOTSUPPORTED;
496                 break;
497         }
498         SM_Initialise(state);
499
500         SetEcho(&data, state);
501
502         GSM_DataClear(&data);
503         data.Model = model;
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;
510
511         /*
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);
516         */
517
518         return GE_NONE;
519 }
520
521  
522 /*
523  * increments the argument until a char unequal to
524  * <cr> or <lf> is found. returns the new position.
525  */
526  
527 char *skipcrlf(char *str)
528 {
529         if (str == NULL)
530                 return str;
531         while ((*str == '\n') || (*str == '\r'))
532                 str++;
533         return str;
534 }
535  
536  
537 /*
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
542  * <lf> was found.
543  * TODO should ask for maximum length.
544  */
545  
546 char *findcrlf(char *str, int test)
547 {
548         if (str == NULL)
549                 return str;
550         while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test))
551                 str++;
552         if (*str == '\0')
553                 return NULL;
554         return str;
555 }