48d39bbddf9145f774468b293b86a1504a1919a5
[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.2.1  2001/11/27 04:37:59  short
18   Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
19
20   Revision 1.1.1.2  2001/11/27 04:19:30  short
21   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 05:17 CET 2001
22
23   Revision 1.7  2001/11/26 18:06:08  pkot
24   Checking for *printf functions, N_(x) for localization, generic ARRAY_LEN, SAFE_STRNCPY, G_GNUC_PRINTF (Jan Kratochvil)
25
26   Revision 1.6  2001/11/19 13:03:18  pkot
27   nk3110.c cleanup
28
29   Revision 1.5  2001/11/08 16:49:19  pkot
30   Cleanups
31
32   Revision 1.4  2001/08/20 23:36:27  pkot
33   More cleanup in AT code (Manfred Jonsson)
34
35   Revision 1.3  2001/08/20 23:27:37  pkot
36   Add hardware shakehand to the link layer (Manfred Jonsson)
37
38   Revision 1.2  2001/08/09 11:51:39  pkot
39   Generic AT support updates and cleanup (Manfred Jonsson)
40
41   Revision 1.1  2001/07/27 00:02:21  pkot
42   Generic AT support for the new structure (Manfred Jonsson)
43
44 */
45
46 #include <string.h>
47 #include <stdlib.h>
48 #include <ctype.h>
49
50 #include "misc.h"
51 #include "gsm-common.h"
52 #include "gsm-statemachine.h"
53 #include "gsm-encoding.h"
54 #include "phones/generic.h"
55 #include "phones/atgen.h"
56 #include "phones/ateric.h"
57 #include "phones/atsie.h"
58 #include "phones/atnok.h"
59 #include "links/atbus.h"
60 #include "links/cbus.h"
61
62
63 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
64 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
65 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
66 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
67 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
68 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
69 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
70 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
71
72 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
73 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
74 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
75 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
76 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
77 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state);
78 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state);
79 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state);
80 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state);
81
82 typedef struct {
83         int gop;
84         AT_SendFunctionType sfunc;
85         GSM_RecvFunctionType rfunc;
86 } AT_FunctionInitType;
87
88
89 /* Mobile phone information */
90 static AT_SendFunctionType AT_Functions[GOP_Max];
91 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
92 static AT_FunctionInitType AT_FunctionInit[] = {
93         { GOP_Init, NULL, Reply },
94         { GOP_GetModel, AT_GetModel, ReplyIdentify },
95         { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
96         { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
97         { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
98         { GOP_Identify, AT_Identify, ReplyIdentify },
99         { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
100         { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
101         { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
102         { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
103         { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
104 };
105
106
107 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
108         if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
109
110
111 GSM_Phone phone_at = {
112         IncomingFunctions,
113         PGEN_IncomingDefault,
114         {
115                 "AT|AT-HW|dancall",                     /* Supported models */
116                 99,                     /* Max RF Level */
117                 0,                      /* Min RF Level */
118                 GRF_CSQ,                /* RF level units */
119                 100,                    /* Max Battery Level */
120                 0,                      /* Min Battery Level */
121                 GBU_Percentage,         /* Battery level units */
122                 0,                      /* Have date/time support */
123                 0,                      /* Alarm supports time only */
124                 0,                      /* Alarms available - FIXME */
125                 0, 0,                   /* Startup logo size - FIXME */
126                 0, 0,                   /* Op logo size */
127                 0, 0                    /* Caller logo size */
128         },
129         Functions
130 };
131
132
133 static GSM_MemoryType memorytype = GMT_XX;
134 static int atcharset = 0;
135
136 static char *memorynames[] = {
137         "ME", /* Internal memory of the mobile equipment */
138         "SM", /* SIM card memory */
139         "FD", /* Fixed dial numbers */
140         "ON", /* Own numbers */
141         "EN", /* Emergency numbers */
142         "DC", /* Dialled numbers */
143         "RC", /* Received numbers */
144         "MC", /* Missed numbers */
145         "LD", /* Last dialed */
146         "MT", /* combined ME and SIM phonebook */
147         "TA", /* for compatibility only: TA=computer memory */
148         "CB", /* Currently selected memory */
149 };
150
151 /* LinkOK is always true for now... */
152 bool ATGEN_LinkOK = true;
153
154
155 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
156 {
157         static int pos = 0;
158         int i;
159         GSM_RecvFunctionType oldfunc;
160
161         if (type >= GOP_Max) {
162                 return (GSM_RecvFunctionType) -1;
163         }
164         if (pos == 0) {
165                 IncomingFunctions[pos].MessageType = type;
166                 IncomingFunctions[pos].Functions = func;
167                 pos++;
168                 return NULL;
169         }
170         for (i=0; i < pos; i++) {
171                 if (IncomingFunctions[i].MessageType == type) {
172                         oldfunc = IncomingFunctions[i].Functions;
173                         IncomingFunctions[i].Functions = func;
174                         return oldfunc;
175                 }
176         }
177         if (pos < GOP_Max-1) {
178                 IncomingFunctions[pos].MessageType = type;
179                 IncomingFunctions[pos].Functions = func;
180                 pos++;
181         }
182         return NULL;
183 }
184
185
186 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
187 {
188         AT_SendFunctionType f;
189
190         f = AT_Functions[type];
191         AT_Functions[type] = func;
192         return f;
193 }
194
195
196 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
197 {
198         char req[128];
199
200         sprintf(req, "ATE1\r\n");
201         if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
202         return SM_Block(state, data, GOP_Init);
203 }
204
205
206 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
207 {
208         char req[128];
209         GSM_Error ret = GE_NONE;
210         GSM_Data data;
211
212         if (mt != memorytype) {
213                 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
214                 ret = SM_SendMessage(state, 14, GOP_Init, req);
215                 if (ret != GE_NONE)
216                         return GE_NOTREADY;
217                 GSM_DataClear(&data);
218                 ret = SM_Block(state, &data, GOP_Init);
219                 if (ret == GE_NONE)
220                         memorytype = mt;
221         }
222         return ret;
223 }
224
225
226 static GSM_Error SetCharset(GSM_Statemachine *state)
227 {
228         char req[128];
229         GSM_Error ret = GE_NONE;
230         GSM_Data data;
231
232         if (atcharset == 0) {
233                 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
234                 ret = SM_SendMessage(state, 15, GOP_Init, req);
235                 if (ret != GE_NONE)
236                         return GE_NOTREADY;
237                 GSM_DataClear(&data);
238                 ret = SM_Block(state, &data, GOP_Init);
239                 if (ret == GE_NONE)
240                         atcharset = 1;
241         }
242         return ret;
243 }
244
245
246 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
247 {
248         GSM_Error ret;
249
250         if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
251                 return ret;
252         if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
253                 return ret;
254         if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
255                 return ret;
256         return Functions(GOP_GetImei, data, state);
257 }
258
259
260 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
261 {
262         char req[128];
263
264         sprintf(req, "AT+CGMM\r\n");
265         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
266                 return GE_NOTREADY;
267         return SM_Block(state, data, GOP_Identify);
268 }
269
270
271 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
272 {
273         char req[128];
274
275         sprintf(req, "AT+CGMI\r\n");
276         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
277                 return GE_NOTREADY;
278         return SM_Block(state, data, GOP_Identify);
279 }
280
281
282 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
283 {
284         char req[128];
285
286         sprintf(req, "AT+CGMR\r\n");
287         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
288                 return GE_NOTREADY;
289         return SM_Block(state, data, GOP_Identify);
290 }
291
292
293 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
294 {
295         char req[128];
296
297         sprintf(req, "AT+CGSN\r\n");
298         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
299                 return GE_NOTREADY;
300         return SM_Block(state, data, GOP_Identify);
301 }
302
303
304 /* gets battery level and power source */
305
306 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state)
307 {
308         char req[128];
309
310         sprintf(req, "AT+CBC\r\n");
311         if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
312                 return GE_NOTREADY;
313         return SM_Block(state, data, GOP_GetBatteryLevel);
314 }
315
316
317 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state)
318 {
319         char req[128];
320  
321         sprintf(req, "AT+CSQ\r\n");
322         if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
323                 return GE_NOTREADY;
324         return SM_Block(state, data, GOP_GetRFLevel);
325 }
326  
327  
328 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state)
329 {
330         char req[128];
331         GSM_Error ret;
332
333         ret = AT_SetMemoryType(data->MemoryStatus->MemoryType,  state);
334         if (ret != GE_NONE)
335                 return ret;
336         sprintf(req, "AT+CPBS?\r\n");
337         if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
338                 return GE_NOTREADY;
339         ret = SM_Block(state, data, GOP_GetMemoryStatus);
340         if (ret != GE_UNKNOWN)
341                 return ret;
342         sprintf(req, "AT+CPBR=?\r\n");
343         if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
344                 return GE_NOTREADY;
345         ret = SM_Block(state, data, GOP_GetMemoryStatus);
346         return ret;
347 }
348
349
350 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state)
351 {
352         char req[128];
353         GSM_Error ret;
354
355         ret = SetCharset(state);
356         if (ret != GE_NONE)
357                 return ret;
358         ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType,  state);
359         if (ret != GE_NONE)
360                 return ret;
361         sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
362         if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
363                 return GE_NOTREADY;
364         ret = SM_Block(state, data, GOP_ReadPhonebook);
365         return ret;
366 }
367
368
369 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
370 {
371         if (op == GOP_Init)
372                 return Initialise(data, state);
373         if ((op > GOP_Init) && (op < GOP_Max))
374                 if (AT_Functions[op])
375                         return (*AT_Functions[op])(data, state);
376         return GE_NOTIMPLEMENTED;
377 }
378
379
380 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
381 {
382         AT_LineBuffer buf;
383         char *pos, *endpos;
384         int l;
385
386         buf.line1 = buffer;
387         buf.length= length;
388         splitlines(&buf);
389         if (buf.line1 == NULL)
390                 return GE_INVALIDPHBOOKLOCATION;
391
392         if (strncmp(buffer, "AT+CPBR", 7)) {
393                 return GE_NONE; /*FIXME*/
394         }
395
396         if (!strncmp(buf.line2, "OK", 2)) {
397                 if (data->PhonebookEntry) {
398                         *(data->PhonebookEntry->Number) = '\0';
399                         *(data->PhonebookEntry->Name) = '\0';
400                         data->PhonebookEntry->Group = 0;
401                         data->PhonebookEntry->SubEntriesCount = 0;
402                 }
403                 return GE_NONE;
404         }
405         if (data->PhonebookEntry) {
406                 data->PhonebookEntry->Group = 0;
407                 data->PhonebookEntry->SubEntriesCount = 0;
408                 pos = strchr(buf.line2, '\"');
409                 endpos = NULL;
410                 if (pos)
411                         endpos = strchr(++pos, '\"');
412                 if (endpos) {
413                         *endpos = '\0';
414                         strcpy(data->PhonebookEntry->Number, pos);
415                 }
416                 pos = NULL;
417                 if (endpos)
418                         pos = strchr(++endpos, '\"');
419                 endpos = NULL;
420                 if (pos) {
421                         pos++;
422                         l = pos - (char *)buffer;
423                         endpos = memchr(pos, '\"', length - l);
424                 }
425                 if (endpos) {
426                         l = endpos - pos;
427                         DecodeAscii(data->PhonebookEntry->Name, pos, l);
428                         *(data->PhonebookEntry->Name + l) = '\0';
429                 }
430         }
431         return GE_NONE;
432 }
433
434
435 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
436 {
437         AT_LineBuffer buf;
438         char *pos;
439
440         buf.line1 = buffer;
441         buf.length= length;
442         splitlines(&buf);
443         if (buf.line1 == NULL)
444                 return GE_INVALIDMEMORYTYPE;
445
446         if (data->MemoryStatus) {
447                 if (strstr(buf.line2,"+CPBS")) {
448                         pos = strchr(buf.line2, ',');
449                         if (pos) {
450                                 data->MemoryStatus->Used = atoi(++pos);
451                         } else {
452                                 data->MemoryStatus->Used = 100;
453                                 data->MemoryStatus->Free = 0;
454                                 return GE_UNKNOWN;
455                         }
456                         pos = strchr(pos, ',');
457                         if (pos) {
458                                 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
459                         } else {
460                                 return GE_UNKNOWN;
461                         }
462                 }
463         }
464         return GE_NONE;
465 }
466
467
468 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
469 {
470         AT_LineBuffer buf;
471         char *pos;
472
473         buf.line1 = buffer;
474         buf.length= length;
475         splitlines(&buf);
476         if ((buf.line1 == NULL) || (buf.line2 == NULL))
477                 return GE_NONE;
478
479         if (!strncmp(buffer, "AT+CBC", 6)) {
480                 if (data->BatteryLevel) {
481                         *(data->BatteryUnits) = GBU_Percentage;
482                         pos = strchr(buf.line2, ',');
483                         if (pos) {
484                                 pos++;
485                                 *(data->BatteryLevel) = atoi(pos);
486                         } else {
487                                 *(data->BatteryLevel) = 1;
488                         }
489                 }
490                 if (data->PowerSource) {
491                         *(data->PowerSource) = 0;
492                         if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
493                         if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
494                 }
495         }
496         return GE_NONE;
497 }
498
499
500 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
501 {
502         AT_LineBuffer buf;
503         char *pos1, *pos2;
504
505         buf.line1 = buffer;
506         buf.length= length;
507         splitlines(&buf);
508         if (buf.line1 == NULL)
509                 return GE_NONE;
510
511         if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
512                 *(data->RFUnits) = GRF_CSQ;
513                 pos1 = buf.line2 + 6;
514                 pos2 = strchr(buf.line2, ',');
515                 if (pos1 < pos2) {
516                         *(data->RFLevel) = atoi(pos1);
517                 } else {
518                         *(data->RFLevel) = 1;
519                 }
520         }
521         return GE_NONE;
522 }
523
524
525 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
526 {
527         AT_LineBuffer buf;
528
529         buf.line1 = buffer;
530         buf.length= length;
531         splitlines(&buf);
532         if (buf.line1 == NULL)
533                 return GE_NONE;         /* Fixme */
534         if (!strncmp(buffer, "AT+CG", 5)) {
535                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
536                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
537                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
538                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
539         }
540         return GE_NONE;
541 }
542
543
544 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
545 {
546         AT_LineBuffer buf;
547         int error = 0;
548
549         buf.line1 = buffer;
550         buf.length= length;
551         splitlines(&buf);
552         if (buf.line1 == NULL)
553                 error = 1;
554
555         return GE_NONE;
556 }
557
558
559 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
560 {
561         GSM_Data data;
562         GSM_Error ret;
563         char model[20];
564         char manufacturer[20];
565         int i;
566
567         fprintf(stderr, "Initializing AT capable mobile phone ...\n");
568
569         /* Copy in the phone info */
570         memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
571
572         for (i=0; i<GOP_Max; i++) {
573                 AT_Functions[i] = NULL;
574                 IncomingFunctions[i].MessageType = 0;
575                 IncomingFunctions[i].Functions = NULL;
576         }
577         for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
578                 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
579                 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
580         }
581
582         switch (state->Link.ConnectionType) {
583         case GCT_Serial:
584                 if (!strcmp(setupdata->Model, "dancall"))
585                         CBUS_Initialise(state);
586                 else if (!strcmp(setupdata->Model, "AT-HW"))
587                         ATBUS_Initialise(state, true);
588                 else
589                         ATBUS_Initialise(state, false);
590                 break;
591         default:
592                 return GE_NOTSUPPORTED;
593                 break;
594         }
595         SM_Initialise(state);
596
597         SetEcho(&data, state);
598
599         GSM_DataClear(&data);
600         data.Model = model;
601         ret = state->Phone.Functions(GOP_GetModel, &data, state);
602         if (ret != GE_NONE) return ret;
603         GSM_DataClear(&data);
604         data.Manufacturer = manufacturer;
605         ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
606         if (ret != GE_NONE) return ret;
607
608         if (!strncasecmp(manufacturer, "ericsson", 8))
609                 AT_InitEricsson(state, model, setupdata->Model);
610         if (!strncasecmp(manufacturer, "siemens", 7))
611                 AT_InitSiemens(state, model, setupdata->Model);
612         if (!strncasecmp(manufacturer, "nokia", 5))
613                 AT_InitNokia(state, model, setupdata->Model);
614
615         return GE_NONE;
616 }
617
618  
619 void splitlines(AT_LineBuffer *buf)
620 {
621         char *pos;
622
623         if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
624                 buf->line1 = NULL;
625                 return;
626         }
627         pos = findcrlf(buf->line1, 0, buf->length);
628         if (pos) {
629                 *pos = 0;
630                 buf->line2 = skipcrlf(++pos);
631         } else {
632                 buf->line2 = buf->line1;
633         }
634         pos = findcrlf(buf->line2, 1, buf->length);
635         if (pos) {
636                 *pos = 0;
637                 buf->line3 = skipcrlf(++pos);
638         } else {
639                 buf->line3 = buf->line2;
640         }
641 }
642
643
644 /*
645  * increments the argument until a char unequal to
646  * <cr> or <lf> is found. returns the new position.
647  */
648  
649 char *skipcrlf(char *str)
650 {
651         if (str == NULL)
652                 return str;
653         while ((*str == '\n') || (*str == '\r'))
654                 str++;
655         return str;
656 }
657  
658  
659 /*
660  * searches for <cr> or <lf> and returns the first
661  * occurrence. if test is set, the gsm char @ which
662  * is 0x00 is not considered as end of string.
663  * return NULL if no <cr> or <lf> was found in the
664  * range of max bytes.
665  */
666  
667 char *findcrlf(char *str, int test, int max)
668 {
669         if (str == NULL)
670                 return str;
671         while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
672                 str++;
673                 max--;
674         }
675         if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))
676                 return NULL;
677         return str;
678 }