Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
[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.1.8.1  2001/11/27 21:30:44  short
18   Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
19
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
22
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
25
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)
28
29   Revision 1.6  2001/11/19 13:03:18  pkot
30   nk3110.c cleanup
31
32   Revision 1.5  2001/11/08 16:49:19  pkot
33   Cleanups
34
35   Revision 1.4  2001/08/20 23:36:27  pkot
36   More cleanup in AT code (Manfred Jonsson)
37
38   Revision 1.3  2001/08/20 23:27:37  pkot
39   Add hardware shakehand to the link layer (Manfred Jonsson)
40
41   Revision 1.2  2001/08/09 11:51:39  pkot
42   Generic AT support updates and cleanup (Manfred Jonsson)
43
44   Revision 1.1  2001/07/27 00:02:21  pkot
45   Generic AT support for the new structure (Manfred Jonsson)
46
47 */
48
49 #include <string.h>
50 #include <stdlib.h>
51 #include <ctype.h>
52
53 #include "misc.h"
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"
64
65
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);
74
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);
84
85 typedef struct {
86         int gop;
87         AT_SendFunctionType sfunc;
88         GSM_RecvFunctionType rfunc;
89 } AT_FunctionInitType;
90
91
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 },
107 };
108
109
110 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
111         if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
112
113
114 GSM_Phone phone_at = {
115         IncomingFunctions,
116         PGEN_IncomingDefault,
117         {
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 */
131         },
132         Functions
133 };
134
135
136 static GSM_MemoryType memorytype = GMT_XX;
137 static int atcharset = 0;
138
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 */
152 };
153
154 /* LinkOK is always true for now... */
155 bool ATGEN_LinkOK = true;
156
157
158 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
159 {
160         static int pos = 0;
161         int i;
162         GSM_RecvFunctionType oldfunc;
163
164         if (type >= GOP_Max) {
165                 return (GSM_RecvFunctionType) -1;
166         }
167         if (pos == 0) {
168                 IncomingFunctions[pos].MessageType = type;
169                 IncomingFunctions[pos].Functions = func;
170                 pos++;
171                 return NULL;
172         }
173         for (i=0; i < pos; i++) {
174                 if (IncomingFunctions[i].MessageType == type) {
175                         oldfunc = IncomingFunctions[i].Functions;
176                         IncomingFunctions[i].Functions = func;
177                         return oldfunc;
178                 }
179         }
180         if (pos < GOP_Max-1) {
181                 IncomingFunctions[pos].MessageType = type;
182                 IncomingFunctions[pos].Functions = func;
183                 pos++;
184         }
185         return NULL;
186 }
187
188
189 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
190 {
191         AT_SendFunctionType f;
192
193         f = AT_Functions[type];
194         AT_Functions[type] = func;
195         return f;
196 }
197
198
199 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
200 {
201         char req[128];
202
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);
206 }
207
208
209 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
210 {
211         char req[128];
212         GSM_Error ret = GE_NONE;
213         GSM_Data data;
214
215         if (mt != memorytype) {
216                 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
217                 ret = SM_SendMessage(state, 14, GOP_Init, req);
218                 if (ret != GE_NONE)
219                         return GE_NOTREADY;
220                 GSM_DataClear(&data);
221                 ret = SM_Block(state, &data, GOP_Init);
222                 if (ret == GE_NONE)
223                         memorytype = mt;
224         }
225         return ret;
226 }
227
228
229 static GSM_Error SetCharset(GSM_Statemachine *state)
230 {
231         char req[128];
232         GSM_Error ret = GE_NONE;
233         GSM_Data data;
234
235         if (atcharset == 0) {
236                 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
237                 ret = SM_SendMessage(state, 15, GOP_Init, req);
238                 if (ret != GE_NONE)
239                         return GE_NOTREADY;
240                 GSM_DataClear(&data);
241                 ret = SM_Block(state, &data, GOP_Init);
242                 if (ret == GE_NONE)
243                         atcharset = 1;
244         }
245         return ret;
246 }
247
248
249 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
250 {
251         GSM_Error ret;
252
253         if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
254                 return ret;
255         if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
256                 return ret;
257         if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
258                 return ret;
259         return Functions(GOP_GetImei, data, state);
260 }
261
262
263 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
264 {
265         char req[128];
266
267         sprintf(req, "AT+CGMM\r\n");
268         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
269                 return GE_NOTREADY;
270         return SM_Block(state, data, GOP_Identify);
271 }
272
273
274 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
275 {
276         char req[128];
277
278         sprintf(req, "AT+CGMI\r\n");
279         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
280                 return GE_NOTREADY;
281         return SM_Block(state, data, GOP_Identify);
282 }
283
284
285 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
286 {
287         char req[128];
288
289         sprintf(req, "AT+CGMR\r\n");
290         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
291                 return GE_NOTREADY;
292         return SM_Block(state, data, GOP_Identify);
293 }
294
295
296 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
297 {
298         char req[128];
299
300         sprintf(req, "AT+CGSN\r\n");
301         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
302                 return GE_NOTREADY;
303         return SM_Block(state, data, GOP_Identify);
304 }
305
306
307 /* gets battery level and power source */
308
309 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state)
310 {
311         char req[128];
312
313         sprintf(req, "AT+CBC\r\n");
314         if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
315                 return GE_NOTREADY;
316         return SM_Block(state, data, GOP_GetBatteryLevel);
317 }
318
319
320 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state)
321 {
322         char req[128];
323  
324         sprintf(req, "AT+CSQ\r\n");
325         if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
326                 return GE_NOTREADY;
327         return SM_Block(state, data, GOP_GetRFLevel);
328 }
329  
330  
331 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state)
332 {
333         char req[128];
334         GSM_Error ret;
335
336         ret = AT_SetMemoryType(data->MemoryStatus->MemoryType,  state);
337         if (ret != GE_NONE)
338                 return ret;
339         sprintf(req, "AT+CPBS?\r\n");
340         if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
341                 return GE_NOTREADY;
342         ret = SM_Block(state, data, GOP_GetMemoryStatus);
343         if (ret != GE_UNKNOWN)
344                 return ret;
345         sprintf(req, "AT+CPBR=?\r\n");
346         if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
347                 return GE_NOTREADY;
348         ret = SM_Block(state, data, GOP_GetMemoryStatus);
349         return ret;
350 }
351
352
353 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state)
354 {
355         char req[128];
356         GSM_Error ret;
357
358         ret = SetCharset(state);
359         if (ret != GE_NONE)
360                 return ret;
361         ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType,  state);
362         if (ret != GE_NONE)
363                 return ret;
364         sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
365         if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
366                 return GE_NOTREADY;
367         ret = SM_Block(state, data, GOP_ReadPhonebook);
368         return ret;
369 }
370
371
372 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
373 {
374         if (op == GOP_Init)
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;
380 }
381
382
383 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
384 {
385         AT_LineBuffer buf;
386         char *pos, *endpos;
387         int l;
388
389         buf.line1 = buffer;
390         buf.length= length;
391         splitlines(&buf);
392         if (buf.line1 == NULL)
393                 return GE_INVALIDPHBOOKLOCATION;
394
395         if (strncmp(buffer, "AT+CPBR", 7)) {
396                 return GE_NONE; /*FIXME*/
397         }
398
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;
405                 }
406                 return GE_NONE;
407         }
408         if (data->PhonebookEntry) {
409                 data->PhonebookEntry->Group = 0;
410                 data->PhonebookEntry->SubEntriesCount = 0;
411                 pos = strchr(buf.line2, '\"');
412                 endpos = NULL;
413                 if (pos)
414                         endpos = strchr(++pos, '\"');
415                 if (endpos) {
416                         *endpos = '\0';
417                         strcpy(data->PhonebookEntry->Number, pos);
418                 }
419                 pos = NULL;
420                 if (endpos)
421                         pos = strchr(++endpos, '\"');
422                 endpos = NULL;
423                 if (pos) {
424                         pos++;
425                         l = pos - (char *)buffer;
426                         endpos = memchr(pos, '\"', length - l);
427                 }
428                 if (endpos) {
429                         l = endpos - pos;
430                         DecodeAscii(data->PhonebookEntry->Name, pos, l);
431                         *(data->PhonebookEntry->Name + l) = '\0';
432                 }
433         }
434         return GE_NONE;
435 }
436
437
438 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
439 {
440         AT_LineBuffer buf;
441         char *pos;
442
443         buf.line1 = buffer;
444         buf.length= length;
445         splitlines(&buf);
446         if (buf.line1 == NULL)
447                 return GE_INVALIDMEMORYTYPE;
448
449         if (data->MemoryStatus) {
450                 if (strstr(buf.line2,"+CPBS")) {
451                         pos = strchr(buf.line2, ',');
452                         if (pos) {
453                                 data->MemoryStatus->Used = atoi(++pos);
454                         } else {
455                                 data->MemoryStatus->Used = 100;
456                                 data->MemoryStatus->Free = 0;
457                                 return GE_UNKNOWN;
458                         }
459                         pos = strchr(pos, ',');
460                         if (pos) {
461                                 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
462                         } else {
463                                 return GE_UNKNOWN;
464                         }
465                 }
466         }
467         return GE_NONE;
468 }
469
470
471 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
472 {
473         AT_LineBuffer buf;
474         char *pos;
475
476         buf.line1 = buffer;
477         buf.length= length;
478         splitlines(&buf);
479         if ((buf.line1 == NULL) || (buf.line2 == NULL))
480                 return GE_NONE;
481
482         if (!strncmp(buffer, "AT+CBC", 6)) {
483                 if (data->BatteryLevel) {
484                         *(data->BatteryUnits) = GBU_Percentage;
485                         pos = strchr(buf.line2, ',');
486                         if (pos) {
487                                 pos++;
488                                 *(data->BatteryLevel) = atoi(pos);
489                         } else {
490                                 *(data->BatteryLevel) = 1;
491                         }
492                 }
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;
497                 }
498         }
499         return GE_NONE;
500 }
501
502
503 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
504 {
505         AT_LineBuffer buf;
506         char *pos1, *pos2;
507
508         buf.line1 = buffer;
509         buf.length= length;
510         splitlines(&buf);
511         if (buf.line1 == NULL)
512                 return GE_NONE;
513
514         if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
515                 *(data->RFUnits) = GRF_CSQ;
516                 pos1 = buf.line2 + 6;
517                 pos2 = strchr(buf.line2, ',');
518                 if (pos1 < pos2) {
519                         *(data->RFLevel) = atoi(pos1);
520                 } else {
521                         *(data->RFLevel) = 1;
522                 }
523         }
524         return GE_NONE;
525 }
526
527
528 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
529 {
530         AT_LineBuffer buf;
531
532         buf.line1 = buffer;
533         buf.length= length;
534         splitlines(&buf);
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);
542         }
543         return GE_NONE;
544 }
545
546
547 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
548 {
549         AT_LineBuffer buf;
550         int error = 0;
551
552         buf.line1 = buffer;
553         buf.length= length;
554         splitlines(&buf);
555         if (buf.line1 == NULL)
556                 error = 1;
557
558         return GE_NONE;
559 }
560
561
562 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
563 {
564         GSM_Data data;
565         GSM_Error ret;
566         char model[20];
567         char manufacturer[20];
568         int i;
569
570         fprintf(stderr, "Initializing AT capable mobile phone ...\n");
571
572         /* Copy in the phone info */
573         memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
574
575         for (i=0; i<GOP_Max; i++) {
576                 AT_Functions[i] = NULL;
577                 IncomingFunctions[i].MessageType = 0;
578                 IncomingFunctions[i].Functions = NULL;
579         }
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);
583         }
584
585         switch (state->Link.ConnectionType) {
586         case GCT_Serial:
587                 if (!strcmp(setupdata->Model, "dancall"))
588                         CBUS_Initialise(state);
589                 else if (!strcmp(setupdata->Model, "AT-HW"))
590                         ATBUS_Initialise(state, true);
591                 else
592                         ATBUS_Initialise(state, false);
593                 break;
594         default:
595                 return GE_NOTSUPPORTED;
596                 break;
597         }
598         SM_Initialise(state);
599
600         SetEcho(&data, state);
601
602         GSM_DataClear(&data);
603         data.Model = model;
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;
610
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);
617
618         return GE_NONE;
619 }
620
621  
622 void splitlines(AT_LineBuffer *buf)
623 {
624         char *pos;
625
626         if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
627                 buf->line1 = NULL;
628                 return;
629         }
630         pos = findcrlf(buf->line1, 0, buf->length);
631         if (pos) {
632                 *pos = 0;
633                 buf->line2 = skipcrlf(++pos);
634         } else {
635                 buf->line2 = buf->line1;
636         }
637         pos = findcrlf(buf->line2, 1, buf->length);
638         if (pos) {
639                 *pos = 0;
640                 buf->line3 = skipcrlf(++pos);
641         } else {
642                 buf->line3 = buf->line2;
643         }
644 }
645
646
647 /*
648  * increments the argument until a char unequal to
649  * <cr> or <lf> is found. returns the new position.
650  */
651  
652 char *skipcrlf(char *str)
653 {
654         if (str == NULL)
655                 return str;
656         while ((*str == '\n') || (*str == '\r'))
657                 str++;
658         return str;
659 }
660  
661  
662 /*
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.
668  */
669  
670 char *findcrlf(char *str, int test, int max)
671 {
672         if (str == NULL)
673                 return str;
674         while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
675                 str++;
676                 max--;
677         }
678         if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))
679                 return NULL;
680         return str;
681 }