:pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Fri Dec 14 20:46 CET 2001
[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 */
17
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21
22 #include "misc.h"
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"
33
34
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);
44
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);
55
56 typedef struct {
57         int gop;
58         AT_SendFunctionType sfunc;
59         GSM_RecvFunctionType rfunc;
60 } AT_FunctionInitType;
61
62
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 }
79 };
80
81
82 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
83         if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
84
85
86 GSM_Phone phone_at = {
87         IncomingFunctions,
88         PGEN_IncomingDefault,
89         {
90                 "AT|AT-HW|dancall",                     /* Supported models */
91                 99,                     /* Max RF Level */
92                 0,                      /* Min 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 */
103         },
104         Functions
105 };
106
107
108 static GSM_MemoryType memorytype = GMT_XX;
109 static int atcharset = 0;
110
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 */
124 };
125
126 /* LinkOK is always true for now... */
127 bool ATGEN_LinkOK = true;
128
129
130 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
131 {
132         static int pos = 0;
133         int i;
134         GSM_RecvFunctionType oldfunc;
135
136         if (type >= GOP_Max) {
137                 return (GSM_RecvFunctionType) -1;
138         }
139         if (pos == 0) {
140                 IncomingFunctions[pos].MessageType = type;
141                 IncomingFunctions[pos].Functions = func;
142                 pos++;
143                 return NULL;
144         }
145         for (i=0; i < pos; i++) {
146                 if (IncomingFunctions[i].MessageType == type) {
147                         oldfunc = IncomingFunctions[i].Functions;
148                         IncomingFunctions[i].Functions = func;
149                         return oldfunc;
150                 }
151         }
152         if (pos < GOP_Max-1) {
153                 IncomingFunctions[pos].MessageType = type;
154                 IncomingFunctions[pos].Functions = func;
155                 pos++;
156         }
157         return NULL;
158 }
159
160
161 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
162 {
163         AT_SendFunctionType f;
164
165         f = AT_Functions[type];
166         AT_Functions[type] = func;
167         return f;
168 }
169
170
171 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
172 {
173         char req[128];
174
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);
178 }
179
180
181 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
182 {
183         char req[128];
184         GSM_Error ret = GE_NONE;
185         GSM_Data data;
186
187         if (mt != memorytype) {
188                 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
189                 ret = SM_SendMessage(state, 14, GOP_Init, req);
190                 if (ret != GE_NONE)
191                         return GE_NOTREADY;
192                 GSM_DataClear(&data);
193                 ret = SM_Block(state, &data, GOP_Init);
194                 if (ret == GE_NONE)
195                         memorytype = mt;
196         }
197         return ret;
198 }
199
200
201 static GSM_Error SetCharset(GSM_Statemachine *state)
202 {
203         char req[128];
204         GSM_Error ret = GE_NONE;
205         GSM_Data data;
206
207         if (atcharset == 0) {
208                 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
209                 ret = SM_SendMessage(state, 15, GOP_Init, req);
210                 if (ret != GE_NONE)
211                         return GE_NOTREADY;
212                 GSM_DataClear(&data);
213                 ret = SM_Block(state, &data, GOP_Init);
214                 if (ret == GE_NONE)
215                         atcharset = 1;
216         }
217         return ret;
218 }
219
220
221 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
222 {
223         GSM_Error ret;
224
225         if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
226                 return ret;
227         if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
228                 return ret;
229         if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
230                 return ret;
231         return Functions(GOP_GetImei, data, state);
232 }
233
234
235 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
236 {
237         char req[128];
238
239         sprintf(req, "AT+CGMM\r\n");
240         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
241                 return GE_NOTREADY;
242         return SM_Block(state, data, GOP_Identify);
243 }
244
245
246 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
247 {
248         char req[128];
249
250         sprintf(req, "AT+CGMI\r\n");
251         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
252                 return GE_NOTREADY;
253         return SM_Block(state, data, GOP_Identify);
254 }
255
256
257 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
258 {
259         char req[128];
260
261         sprintf(req, "AT+CGMR\r\n");
262         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
263                 return GE_NOTREADY;
264         return SM_Block(state, data, GOP_Identify);
265 }
266
267
268 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
269 {
270         char req[128];
271
272         sprintf(req, "AT+CGSN\r\n");
273         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
274                 return GE_NOTREADY;
275         return SM_Block(state, data, GOP_Identify);
276 }
277
278
279 /* gets battery level and power source */
280
281 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state)
282 {
283         char req[128];
284
285         sprintf(req, "AT+CBC\r\n");
286         if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
287                 return GE_NOTREADY;
288         return SM_Block(state, data, GOP_GetBatteryLevel);
289 }
290
291
292 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state)
293 {
294         char req[128];
295  
296         sprintf(req, "AT+CSQ\r\n");
297         if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
298                 return GE_NOTREADY;
299         return SM_Block(state, data, GOP_GetRFLevel);
300 }
301  
302  
303 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state)
304 {
305         char req[128];
306         GSM_Error ret;
307
308         ret = AT_SetMemoryType(data->MemoryStatus->MemoryType,  state);
309         if (ret != GE_NONE)
310                 return ret;
311         sprintf(req, "AT+CPBS?\r\n");
312         if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
313                 return GE_NOTREADY;
314         ret = SM_Block(state, data, GOP_GetMemoryStatus);
315         if (ret != GE_UNKNOWN)
316                 return ret;
317         sprintf(req, "AT+CPBR=?\r\n");
318         if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
319                 return GE_NOTREADY;
320         ret = SM_Block(state, data, GOP_GetMemoryStatus);
321         return ret;
322 }
323
324
325 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state)
326 {
327         char req[128];
328         GSM_Error ret;
329
330         ret = SetCharset(state);
331         if (ret != GE_NONE)
332                 return ret;
333         ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType,  state);
334         if (ret != GE_NONE)
335                 return ret;
336         sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
337         if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
338                 return GE_NOTREADY;
339         ret = SM_Block(state, data, GOP_ReadPhonebook);
340         return ret;
341 }
342
343
344 static GSM_Error AT_CallDivert(GSM_Data *data, GSM_Statemachine *state)
345 {
346         char req[64];
347
348         if (!data->CallDivert) return GE_UNKNOWN;
349
350         sprintf(req, "AT+CCFC=");
351
352         switch (data->CallDivert->DType) {
353         case GSM_CDV_AllTypes:
354                 strcat(req, "4");
355                 break;
356         case GSM_CDV_Busy:
357                 strcat(req, "1");
358                 break;
359         case GSM_CDV_NoAnswer:
360                 strcat(req, "2");
361                 break;
362         case GSM_CDV_OutOfReach:
363                 strcat(req, "3");
364                 break;
365         default:
366                 dprintf("3. %d\n", data->CallDivert->DType);
367                 return GE_NOTIMPLEMENTED;
368         }
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);
375         else
376                 sprintf(req, "%s,%d", req, data->CallDivert->Operation);
377
378         strcat(req, "\r\n");
379
380         dprintf("%s", req);
381         if (SM_SendMessage(state, strlen(req), GOP_CallDivert, req) != GE_NONE)
382                 return GE_NOTREADY;
383         return SM_WaitFor(state, data, GOP_CallDivert);
384 }
385
386 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
387 {
388         if (op == GOP_Init)
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;
394 }
395
396
397 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
398 {
399         AT_LineBuffer buf;
400         char *pos, *endpos;
401         int l;
402
403         buf.line1 = buffer;
404         buf.length= length;
405         splitlines(&buf);
406         if (buf.line1 == NULL)
407                 return GE_INVALIDPHBOOKLOCATION;
408
409         if (strncmp(buffer, "AT+CPBR", 7)) {
410                 return GE_NONE; /*FIXME*/
411         }
412
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;
419                 }
420                 return GE_NONE;
421         }
422         if (data->PhonebookEntry) {
423                 data->PhonebookEntry->Group = 0;
424                 data->PhonebookEntry->SubEntriesCount = 0;
425                 pos = strchr(buf.line2, '\"');
426                 endpos = NULL;
427                 if (pos)
428                         endpos = strchr(++pos, '\"');
429                 if (endpos) {
430                         *endpos = '\0';
431                         strcpy(data->PhonebookEntry->Number, pos);
432                 }
433                 pos = NULL;
434                 if (endpos)
435                         pos = strchr(++endpos, '\"');
436                 endpos = NULL;
437                 if (pos) {
438                         pos++;
439                         l = pos - (char *)buffer;
440                         endpos = memchr(pos, '\"', length - l);
441                 }
442                 if (endpos) {
443                         l = endpos - pos;
444                         DecodeAscii(data->PhonebookEntry->Name, pos, l);
445                         *(data->PhonebookEntry->Name + l) = '\0';
446                 }
447         }
448         return GE_NONE;
449 }
450
451
452 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
453 {
454         AT_LineBuffer buf;
455         char *pos;
456
457         buf.line1 = buffer;
458         buf.length= length;
459         splitlines(&buf);
460         if (buf.line1 == NULL)
461                 return GE_INVALIDMEMORYTYPE;
462
463         if (data->MemoryStatus) {
464                 if (strstr(buf.line2,"+CPBS")) {
465                         pos = strchr(buf.line2, ',');
466                         if (pos) {
467                                 data->MemoryStatus->Used = atoi(++pos);
468                         } else {
469                                 data->MemoryStatus->Used = 100;
470                                 data->MemoryStatus->Free = 0;
471                                 return GE_UNKNOWN;
472                         }
473                         pos = strchr(pos, ',');
474                         if (pos) {
475                                 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
476                         } else {
477                                 return GE_UNKNOWN;
478                         }
479                 }
480         }
481         return GE_NONE;
482 }
483
484
485 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
486 {
487         AT_LineBuffer buf;
488         char *pos;
489
490         buf.line1 = buffer;
491         buf.length= length;
492         splitlines(&buf);
493         if ((buf.line1 == NULL) || (buf.line2 == NULL))
494                 return GE_NONE;
495
496         if (!strncmp(buffer, "AT+CBC", 6)) {
497                 if (data->BatteryLevel) {
498                         *(data->BatteryUnits) = GBU_Percentage;
499                         pos = strchr(buf.line2, ',');
500                         if (pos) {
501                                 pos++;
502                                 *(data->BatteryLevel) = atoi(pos);
503                         } else {
504                                 *(data->BatteryLevel) = 1;
505                         }
506                 }
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;
511                 }
512         }
513         return GE_NONE;
514 }
515
516
517 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
518 {
519         AT_LineBuffer buf;
520         char *pos1, *pos2;
521
522         buf.line1 = buffer;
523         buf.length= length;
524         splitlines(&buf);
525         if (buf.line1 == NULL)
526                 return GE_NONE;
527
528         if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
529                 *(data->RFUnits) = GRF_CSQ;
530                 pos1 = buf.line2 + 6;
531                 pos2 = strchr(buf.line2, ',');
532                 if (pos1 < pos2) {
533                         *(data->RFLevel) = atoi(pos1);
534                 } else {
535                         *(data->RFLevel) = 1;
536                 }
537         }
538         return GE_NONE;
539 }
540
541
542 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
543 {
544         AT_LineBuffer buf;
545
546         buf.line1 = buffer;
547         buf.length= length;
548         splitlines(&buf);
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);
556         }
557         return GE_NONE;
558 }
559
560 static GSM_Error ReplyCallDivert(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
561 {
562         int i;
563         for (i = 0; i < length; i++) {
564                 dprintf("%02x ", buffer[i]);
565         }
566         dprintf("\n");
567         return GE_NONE;
568 }
569
570 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
571 {
572         AT_LineBuffer buf;
573         int error = 0;
574
575         buf.line1 = buffer;
576         buf.length= length;
577         splitlines(&buf);
578         if (buf.line1 == NULL)
579                 error = 1;
580
581         return GE_NONE;
582 }
583
584
585 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
586 {
587         GSM_Data data;
588         GSM_Error ret;
589         char model[20];
590         char manufacturer[20];
591         int i;
592
593         fprintf(stderr, "Initializing AT capable mobile phone ...\n");
594
595         /* Copy in the phone info */
596         memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
597
598         for (i=0; i<GOP_Max; i++) {
599                 AT_Functions[i] = NULL;
600                 IncomingFunctions[i].MessageType = 0;
601                 IncomingFunctions[i].Functions = NULL;
602         }
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);
606         }
607
608         switch (state->Link.ConnectionType) {
609         case GCT_Serial:
610                 if (!strcmp(setupdata->Model, "dancall"))
611                         CBUS_Initialise(state);
612                 else if (!strcmp(setupdata->Model, "AT-HW"))
613                         ATBUS_Initialise(state, true);
614                 else
615                         ATBUS_Initialise(state, false);
616                 break;
617         default:
618                 return GE_NOTSUPPORTED;
619                 break;
620         }
621         SM_Initialise(state);
622
623         SetEcho(&data, state);
624
625         GSM_DataClear(&data);
626         data.Model = model;
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;
633
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);
640
641         return GE_NONE;
642 }
643
644  
645 void splitlines(AT_LineBuffer *buf)
646 {
647         char *pos;
648
649         if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
650                 buf->line1 = NULL;
651                 return;
652         }
653         pos = findcrlf(buf->line1, 0, buf->length);
654         if (pos) {
655                 *pos = 0;
656                 buf->line2 = skipcrlf(++pos);
657         } else {
658                 buf->line2 = buf->line1;
659         }
660         pos = findcrlf(buf->line2, 1, buf->length);
661         if (pos) {
662                 *pos = 0;
663                 buf->line3 = skipcrlf(++pos);
664         } else {
665                 buf->line3 = buf->line2;
666         }
667 }
668
669
670 /*
671  * increments the argument until a char unequal to
672  * <cr> or <lf> is found. returns the new position.
673  */
674  
675 char *skipcrlf(char *str)
676 {
677         if (str == NULL)
678                 return str;
679         while ((*str == '\n') || (*str == '\r'))
680                 str++;
681         return str;
682 }
683  
684  
685 /*
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.
691  */
692  
693 char *findcrlf(char *str, int test, int max)
694 {
695         if (str == NULL)
696                 return str;
697         while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
698                 str++;
699                 max--;
700         }
701         if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))
702                 return NULL;
703         return str;
704 }