sms(s): delete after copy to common directory
[sip-router] / modules_k / sms / libsms_modem.c
1 /*
2 SMS Server Tools
3 Copyright (C) 2000 Stefan Frings
4
5 This program is free software unless you got it under another license directly
6 from the author. You can redistribute it and/or modify it under the terms of
7 the GNU General Public License as published by the Free Software Foundation.
8 Either version 2 of the License, or (at your option) any later version.
9
10 http://www.isis.de/members/~s.frings
11 mailto:s.frings@mail.isis.de
12  */
13
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <strings.h>
20 #include <errno.h>
21 #include <termios.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <syslog.h>
25 #include <sys/ioctl.h>
26 #ifdef __sun
27 #include <sys/filio.h>
28 #endif
29 #include "libsms_modem.h"
30 #include "../../dprint.h"
31
32 #define  MAX_BUF        2048
33 #define  CDS_HDR        "\r\n+CDS:"
34 #define  CDS_HDR_LEN    (strlen(CDS_HDR))
35 #define optz(_n,_l)     (buf+buf_len-(((_n)+(_l)>buf_len)?buf_len:(_n)+(_l)))
36
37 /* global variables */
38 int         sms_report_type;
39 cds_report  cds_report_func;
40
41
42
43 int put_command( struct modem *mdm, char* cmd, int cmd_len, char* answer,
44                                                                                         int max, int timeout,char* exp_end)
45 {
46         static char buf[MAX_BUF];
47         static int  buf_len = 0;
48         char* pos;
49         char* foo;
50         char* ptr;
51         char* to_move;
52         char* answer_s;
53         char* answer_e;
54         int   timeoutcounter;
55         int   available;
56         int   status;
57         int   exp_end_len;
58         int   n;
59
60         /* check if fd is "clean" for reading */
61         timeoutcounter = 0;
62         ioctl(mdm->fd,TIOCMGET,&status);
63         while (!(status & TIOCM_CTS))
64         {
65                 usleep( READ_SLEEP );
66                 timeoutcounter++;
67                 ioctl(mdm->fd,TIOCMGET,&status);
68                 if (timeoutcounter>=timeout) {
69                         LM_INFO("Modem is not clear to send\n");
70                         return 0;
71                 }
72         }
73 #ifdef SHOW_SMS_MODEM_COMMAND
74         LM_DBG("<--<%d>-->[%.*s] \n",cmd_len,cmd_len,cmd);
75 #endif
76         /* send the command to the modem */
77         write(mdm->fd,cmd,cmd_len);
78         tcdrain(mdm->fd);
79
80         /* read from the modem */
81         exp_end_len = exp_end?strlen(exp_end):0;
82         answer_s = buf;
83         answer_e = 0;
84         to_move = 0;
85         do
86         {
87                 /* try to read some bytes */
88                 ioctl(mdm->fd,FIONREAD,&available);
89                 /* how many bytes are available to read? */
90                 if (available<1)  /* if 0 then wait a little bit and retry */
91                 {
92                         usleep( READ_SLEEP );
93                         timeoutcounter++;
94                         ioctl(mdm->fd,FIONREAD,&available);
95                 }
96                 if (available>0)
97                 {
98                         /* How many bytes do I want to read maximum?
99                         Not more than buffer size. And how many bytes are available? */
100                         n = (available>MAX_BUF-buf_len-1)?MAX_BUF-buf_len-1:available;
101                         /* read data */
102                         n = read( mdm->fd, buf+buf_len, n);
103                         if (n<0) {
104                                 LM_ERR("error reading from modem: %s\n",
105                                         strerror(errno));
106                                 goto error;
107                         }
108                         if (n) {
109                                 buf_len += n;
110                                 buf[buf_len] = 0;
111                                 //LM_DBG("read = [%s]\n",buf+buf_len-n);
112                                 foo = pos = 0;
113                                 if ( (!exp_end && ((pos=strstr(optz(n,4),"OK\r\n"))
114                                 || (foo=strstr(optz(n,5),"ERROR"))))
115                                 || (exp_end && (pos=strstr(optz(n,exp_end_len),exp_end)) )) {
116                                         /* we found the end */
117                                         //LM_DBG("end found = %s\n",
118                                         //      (foo?"ERROR":(exp_end?exp_end:"OK")));
119                                         /* for ERROR we still have to read EOL */
120                                         if (!foo || (foo=strstr(foo+5,"\r\n"))) {
121                                                 answer_e = foo?foo+2:(pos+(exp_end?exp_end_len:4));
122                                                 timeoutcounter = timeout;
123                                         }
124                                 }
125                         }
126                 }
127         /* repeat until timout */
128         }while (timeoutcounter<timeout);
129
130         if (!answer_e)
131                 answer_e = buf+buf_len;
132
133         /* CDS report is activ? */
134         if (sms_report_type==CDS_REPORT) {
135                 to_move = 0;
136                 ptr = buf;
137                 /* do we have a CDS reply inside? */
138                 while ( (pos=strstr(ptr,CDS_HDR)) ) {
139                         if (pos!=ptr) {  /* here we have the command response */
140                                 answer_s = ptr;
141                         }
142                         /* look for the end of CDS response */
143                         ptr = pos+CDS_HDR_LEN;
144                         for( n=0 ; n<2&&(foo=strstr(ptr,"\r\n")) ; ptr=foo+2,n++ );
145                         if (n<2) { /* we haven't read the entire CDS response */
146                                 LM_DBG("CDS end not found!\n");
147                                 to_move = pos;
148                                 ptr = buf + buf_len;
149                         }else{
150                                 /* process the CDS */
151                                 LM_DBG("CDS=[%.*s]\n",(int)(ptr-pos),pos);
152                                 cds_report_func(mdm,pos,ptr-pos);
153                         }
154                 }
155                 if ((*ptr)) {
156                         answer_s = ptr;
157                         ptr = answer_e;
158                 }
159                 if (ptr!=buf+buf_len)
160                         to_move = ptr;
161         }
162
163         /* copy the response in answer buffer - as much as fits */
164         if (answer && max) {
165                 n = max-1<answer_e-answer_s?max-1:answer_e-answer_s;
166                 memcpy(answer,answer_s,n);
167                 answer[n] = 0;
168         }
169         /* shift left the remaining data into the buffer - if needs */
170         if (sms_report_type==CDS_REPORT && to_move) {
171                 buf_len = buf_len - (to_move-buf);
172                 memcpy(buf,to_move,buf_len);
173                 buf[buf_len] = 0;
174                 LM_DBG("buffer shifted left=[%d][%s]\n",buf_len,buf);
175         } else {
176                 buf_len = 0;
177         }
178
179 #ifdef SHOW_SMS_MODEM_COMMAND
180         LM_DBG("<-[%s] \n",answer);
181 #endif
182         return answer_e-answer_s;
183
184 error:
185         return 0;
186 }
187
188
189
190
191 /* setup serial port */
192 int setmodemparams( struct modem *mdm )
193 {
194         struct termios newtio;
195
196         bzero(&newtio, sizeof(newtio));
197         newtio.c_cflag = mdm->baudrate | CRTSCTS | CS8 | CLOCAL | CREAD | O_NDELAY;
198         //uncomment next line to disable hardware handshake
199         //newtio.c_cflag &= ~CRTSCTS;
200         newtio.c_iflag = IGNPAR;
201         newtio.c_oflag = 0;
202         newtio.c_lflag = 0;
203         newtio.c_cc[VTIME]    = 1;
204         newtio.c_cc[VMIN]     = 0;
205         tcflush(mdm->fd, TCIOFLUSH);
206         tcsetattr(mdm->fd,TCSANOW,&newtio);
207         return 0;
208 }
209
210
211
212
213 int initmodem(struct modem *mdm, cds_report cds_report_f)
214 {
215         char command[100];
216         char answer[100];
217         int retries=0;
218         int success=0;
219         int clen=0;
220         int n;
221
222         LM_INFO("init modem %s on %s.\n",mdm->name,mdm->device);
223
224         if (mdm->pin[0]) {
225                 /* Checking if modem needs PIN */
226                 put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0);
227                 if (strstr(answer,"+CPIN: SIM PIN")) {
228                         LM_INFO("Modem needs PIN, entering PIN...\n");
229                         clen=sprintf(command,"AT+CPIN=\"%s\"\r",mdm->pin);
230                         put_command(mdm,command,clen,answer,sizeof(answer),100,0);
231                         put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0);
232                         if (!strstr(answer,"+CPIN: READY")) {
233                                 if (strstr(answer,"+CPIN: SIM PIN")) {
234                                         LM_ERR("Modem did not accept this PIN\n");
235                                         goto error;
236                                 } else if (strstr(answer,"+CPIN: SIM PUK")) {
237                                         LM_ERR("YourPIN is locked! Unlock it manually!\n");
238                                         goto error;
239                                 } else {
240                                         goto error;
241                                 }
242                         }
243                         LM_INFO("INFO:initmodem: PIN Ready!\n");
244                         sleep(5);
245                 }
246         }
247
248         if (mdm->mode==MODE_DIGICOM)
249                 success=1;
250         else {
251                 LM_INFO("INFO:initmodem: Checking if Modem is registered to"
252                         " the network\n");
253                 success=0;
254                 retries=0;
255                 do
256                 {
257                         retries++;
258                         put_command(mdm,"AT+CREG?\r",9,answer,sizeof(answer),100,0);
259                         if (strchr(answer,'1') )
260                         {
261                                 LM_INFO("INFO:initmodem: Modem is registered to the"
262                                         " network\n");
263                                 success=1;
264                         } else if (strchr(answer,'2')) {
265                                 // added by bogdan
266                                 LM_WARN("Modems seems to try to reach the network!"
267                                                 " Let's wait a little bit\n");
268                                 retries--;
269                                 sleep(2);
270                         } else if (strchr(answer,'5')) {
271                                 // added by Thomas Stoeckel
272                                 LM_INFO("Modem is registered to a roaming partner network\n");
273                                 success=1;
274                         } else if (strstr(answer,"ERROR")) {
275                                 LM_WARN("Ignoring that modem does not support +CREG command\n");
276                                 success=1;
277                         } else {
278                                 LM_NOTICE("NOTICE:initmodem: Waiting 2 sec. before retrying\n");
279                                 sleep(2);
280                         }
281                 }while ((success==0)&&(retries<20));
282         }
283
284         if (success==0) {
285                 LM_ERR("Modem is not registered to the network\n");
286                 goto error;
287         }
288
289         for( n=0 ; n<2+2*(sms_report_type==CDS_REPORT) ; n++) {
290                 /* build the command */
291                 switch (n) {
292                         case 0:
293                                 strcpy(command,"AT+CMGF=0\r");
294                                 command[8]+=(mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM);
295                                 clen = 10;
296                                 break;
297                         case 1:
298                                 strcpy(command,"AT S7=45 S0=0 L1 V1 X4 &c1 E1 Q0\r");
299                                 clen = 33;
300                                 break;
301                         case 2:
302                                 strcpy(command,"AT+CSMP=49,167,0,241\r");
303                                 clen = 21;
304                                 break;
305                         case 3:
306                                 strcpy(command,"AT+CNMI=1,1,0,1,0\r");
307                                 clen = 18;
308                                 break;
309                 }
310                 /* send it to modem */
311                 retries=0;
312                 success=0;
313                 do {
314                         retries++;
315                         /*querying the modem*/
316                         put_command(mdm,command,clen,answer,sizeof(answer),100,0);
317                         /*dealing with the answer*/
318                         if (strstr(answer,"ERROR")) {
319                                 LM_NOTICE("Waiting 1 sec. before to retrying\n");
320                                 sleep(1);
321                         } else
322                                 success=1;
323                 }while ((success==0)&&(retries<3));
324                 /* have we succeeded? */
325                 if (success==0) {
326                         LM_ERR("cmd [%.*s] returned ERROR\n", clen-1,command);
327                         goto error;
328                 }
329         } /* end for */
330
331         if ( sms_report_type==CDS_REPORT && !cds_report_f) {
332                 LM_ERR("no CDS_REPORT function given\n");
333                 goto error;
334         }
335         cds_report_func = cds_report_f;
336
337         if (mdm->smsc[0]) {
338                 LM_INFO("Changing SMSC to \"%s\"\n",mdm->smsc);
339                 setsmsc(mdm,mdm->smsc);
340         }
341
342
343
344         return 0;
345 error:
346         return -1;
347 }
348
349
350
351
352 int checkmodem(struct modem *mdm)
353 {
354         char answer[500];
355
356         /* Checking if modem needs PIN */
357         put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0);
358         if (!strstr(answer,"+CPIN: READY")) {
359                 LM_WARN("modem wants the PIN again!\n");
360                 goto reinit;
361         }
362
363         if (mdm->mode!=MODE_DIGICOM) {
364                 put_command(mdm,"AT+CREG?\r",9,answer,sizeof(answer),100,0);
365                 if (!strchr(answer,'1') ) {
366                         LM_WARN("Modem is not registered to the"
367                                         " network\n");
368                         goto reinit;
369                 }
370         }
371
372         return 1;
373 reinit:
374         LM_WARN("re -init the modem!!\n");
375         initmodem(mdm,cds_report_func);
376         return -1;
377 }
378
379
380
381
382 int setsmsc(struct modem *mdm, char *smsc)
383 {
384         char command[100];
385         char answer[50];
386         int  clen;
387
388         if (smsc && smsc[0]) {
389                 clen=sprintf(command,"AT+CSCA=\"+%s\"\r",smsc);
390                 put_command(mdm,command,clen,answer,sizeof(answer),50,0);
391         }
392         return 0;
393 }
394
395
396
397
398 int openmodem( struct modem *mdm)
399 {
400         mdm->fd = open(mdm->device, O_RDWR | O_NOCTTY );
401         if (mdm->fd <0)
402                 return -1;
403
404         tcgetattr(mdm->fd,&(mdm->oldtio));
405         return 0;
406 }
407
408
409
410
411 int closemodem(struct modem *mdm)
412 {
413         tcsetattr(mdm->fd,TCSANOW,&(mdm->oldtio));
414         close(mdm->fd);
415         return 0;
416 }
417