first working version
[mopice.git] / mopice.c
1 /* 
2  *  mopice.c
3  * 
4  *  Copyright (C) 2011 
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details. You should have received a
15  * copy of the GNU General Public License along with this program; if
16  * not, write to the Free Software Foundation, Inc, 59 Temple Place - 
17  * Suite 330, Boston, MA 02111-1307, USA.
18  * 
19  */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <time.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <termios.h>
30 #include <unistd.h>
31
32 #include "mopice.h"
33
34 /*
35  *  STRUCTURES 
36  */
37
38 #define CMD_TYPE_MAND 1
39 #define CMD_TYPE_OPT  2
40
41 struct cmd {
42     char *snd;
43     char *repl;
44     int wai;
45     int type;
46 };
47
48
49 /* 
50  *  PROTOTYPES 
51  */
52 void alarm_sh(int sig);
53 int read_reply(int fh, char *reply, time_t secs);
54 int send_cmds(int fh, struct cmd *cmds, int cmds_n);
55 int ring_waiter(int fh, int ring_numb, int ring_inter);
56 int greetings_send(int fh, char *gree);
57
58
59 /*
60  *  GLOBALS
61  */
62 struct cmd init_cmds[] = {
63     { "ATZ",   "OK", 5, CMD_TYPE_MAND }
64     , { "AT",    "OK\r\n", 3, CMD_TYPE_MAND }
65     , { "ATH0",  "OK\r\n", 3, CMD_TYPE_MAND }
66     , { "ATL1",  "OK\r\n", 3, CMD_TYPE_MAND }
67     , { "ATE1",  "OK\r\n", 3, CMD_TYPE_MAND }
68     , { "ATM2",  "OK\r\n", 3, CMD_TYPE_MAND }
69 };
70
71 struct cmd answ_cmds[] = {
72     { "AT+FCLASS=8", "OK\r\n", 3, CMD_TYPE_MAND }
73     , { "AT+VLS=1",    "OK\r\n", 3, CMD_TYPE_MAND }
74     , { "AT+VGT=135",  "OK\r\n", 3, CMD_TYPE_MAND } 
75     , { "AT+VTX",    "CONNECT\r\n", 5, CMD_TYPE_MAND }
76 };
77
78 struct cmd rec_cmds[] = {
79     /* { "ATH1",      "OK", 3, CMD_TYPE_MAND } */
80     { "AT+FCLASS=8", "OK\r\n", 3, CMD_TYPE_MAND }
81     , { "AT+VSD=129,200",  "OK\r\n", 3, CMD_TYPE_MAND }
82     , { "AT+VLS=1",    "OK\r\n", 3, CMD_TYPE_MAND }
83     , { "AT+VRX",    "CONNECT\r\n", 5, CMD_TYPE_MAND }
84 };
85
86 struct cmd recmsg_cmds[] = {
87     { "\x10\x03","OK\r\n", 3, CMD_TYPE_MAND }
88     , { "AT+VSD=129,200",  "OK\r\n", 3, CMD_TYPE_MAND }
89     , { "AT+VGR=255",  "OK\r\n", 3, CMD_TYPE_MAND }
90     , { "AT+VTD=80",  "OK\r\n", 3, CMD_TYPE_MAND }
91     , { "AT+VTS=[933,0]",  "OK\r\n", 3, CMD_TYPE_MAND }
92     , { "AT+VLS=1",    "OK\r\n", 3, CMD_TYPE_MAND }
93     , { "AT+VRX",    "CONNECT\r\n", 5, CMD_TYPE_MAND }
94 };
95
96 struct cmd clo_cmds[] = {
97     { "\x10\x03","OK\r\n", 3, CMD_TYPE_MAND }
98     , { "AT",    "OK\r\n", 3, CMD_TYPE_MAND }
99     , { "AT+VLS=0",    "OK\r\n", 3, CMD_TYPE_MAND }
100     , { "AT+FCLASS=0", "OK\r\n", 3, CMD_TYPE_MAND }
101 };
102
103 int verbose = 0;
104
105
106 /*
107  *  FUNCTIONS
108  */
109
110 void alarm_sh(int sig)
111 {
112     if (verbose >= 3) fprintf(stderr, "CATCHED %d\n", sig);
113 }
114
115 int read_reply(int fh, char *rep, time_t secs)
116 {
117     time_t tout;
118     char bf[1024];
119     int bf_l, ret, i;
120     struct timespec ts;
121
122     if (verbose >= 3) fprintf(stderr, "read_reply::begin\n");
123
124     strcpy(bf,"");
125     bf_l = 0;
126
127     clock_gettime(CLOCK_MONOTONIC, &ts);
128     tout = ts.tv_sec + secs;
129
130     while (1) {
131         clock_gettime(CLOCK_MONOTONIC, &ts);
132         if (ts.tv_sec >= tout) {
133             if (verbose >= 3) fprintf(stderr, "read_reply::end1\n");
134             break;
135         }
136         alarm((unsigned int)secs + 1);
137         if (bf_l == sizeof(bf)) {
138             if (verbose >= 3) fprintf(stderr, "read_reply::end2\n");
139             return (-2);
140         }
141         if (verbose >= 3) fprintf(stderr, "read_reply::pre_read\n");
142         ret = read(fh, &(bf[bf_l]), 512 - bf_l);
143         if (verbose >= 3) fprintf(stderr, "read_reply::post_read %d\n", ret);
144         if (verbose >= 3) {
145             for (i = 0 ; i < ret ; i++) {
146                 fprintf(stderr, "xx %02x ", bf[bf_l+i]);
147                 if (i % 16 == 15) {
148                     fprintf(stderr, "\n");
149                 }
150             }
151             if (i % 16 != 15) 
152                 fprintf(stderr, "\n");
153         }
154         if (ret < 0) {
155             if (errno == EINTR) {
156                 if (verbose >= 3) fprintf(stderr, "read_reply::end3\n");
157                 return (0);
158             }
159             else {
160                 if (verbose >= 3) fprintf(stderr, "read_reply::end4\n");
161                 return (-1);
162             }
163         }
164         alarm(0);
165         bf[bf_l+ret] = '\0';
166         bf_l = strlen(bf);
167         if (verbose >= 3) fprintf(stderr, "bf_l: %d %ld %p\n", bf_l, strlen(rep), strstr(bf, rep));
168         if (strstr(bf, rep)) {
169             if (verbose >= 3) fprintf(stderr, "read_reply::end5\n");
170             if (verbose >= 2) fprintf(stderr, "RET: [%s]\n", bf);
171             return (1);
172         }
173     }
174
175     if (verbose >= 3) fprintf(stderr, "read_reply::end6\n");
176     return (0);
177 }
178
179 int send_cmds(int fh, struct cmd *cmds, int cmds_n)
180 {
181     char bf[512];
182     int i, ret;
183
184     if (verbose >= 3) fprintf(stderr, "send_cmds::begin\n");
185
186     for (i = 0 ; i < cmds_n ; i++) {
187         if (verbose >= 2) fprintf(stderr, "send: [%s](%ld)\n", cmds[i].snd,strlen(cmds[i].snd));
188         sprintf(bf, "%s\r\n", cmds[i].snd);
189         write(fh, bf, strlen(bf));
190         ret = read_reply(fh, cmds[i].repl, cmds[i].wai);
191         
192         if (verbose >= 3) fprintf(stderr, "send_cmds::read_reply return %d\n", ret);
193         if (ret < 0) {
194             if (verbose >= 1) fprintf(stderr, "ERROR: read_reply return %d\n", ret);
195             if (verbose >= 3) fprintf(stderr, "send_cmds::end1\n");
196             return 0;
197         }
198         else if (ret == 0) {
199             if (verbose >= 3) fprintf(stderr, "DE CHE: %d\n", cmds[i].type);
200             if (cmds[i].type == CMD_TYPE_MAND) {
201                 if (verbose >= 3) fprintf(stderr, "send_cmds::end2\n");
202                 return 0;
203             }
204             else if (cmds[i].type == CMD_TYPE_OPT) {
205                 if (verbose >= 1) fprintf(stderr, "WARN: on command %d REPLY [%s] NOT ARRIVED\n", i, cmds[i].repl);
206                 if (verbose >= 3) fprintf(stderr, "send_cmds::end3\n");
207             }
208         }
209     }
210     
211     if (verbose >= 3) fprintf(stderr, "send_cmds::end4\n");
212     return 1;
213 }
214
215
216 #define RING "RING\r\n"
217
218 int ring_waiter(int fh, int ring_numb, int ring_inter)
219 {
220     char bf[512];
221     int ret, ct_cur = 0;
222     struct timespec ts;
223     time_t tout = 0;
224
225     if (verbose >= 3) fprintf(stderr, "%s:: BEGIN\n", __FUNCTION__);
226
227     while ((ret = read(fh, bf, 511)) > 0) {
228         bf[ret] = '\0';
229         if (strstr(bf, RING)) {
230             clock_gettime(CLOCK_MONOTONIC, &ts);
231
232             if (ct_cur > 0) {
233                 if (ts.tv_sec < tout) {
234                     if ((ct_cur + 1) == ring_numb) {
235                         return (1);
236                     }                    
237                 }
238                 else {
239                     ct_cur = 0;
240                 }
241             }
242             tout = ts.tv_sec + ring_inter;
243             ct_cur++;
244         }
245         if (verbose >= 3) fprintf(stderr, "%s:: ct_cur: %d\n", __FUNCTION__, ct_cur);
246     }
247     return 0;
248 }
249
250 int greetings_send(int fh, char *gree)
251 {
252     FILE *fp;
253     int ret, i, e;
254     unsigned char bf[10240], bf2[10240];
255
256     /* sox greetings.wav -e unsigned-integer -b 8 -r 8000 -c 1 greetings.raw */
257     if ((fp = fopen(gree, "r")) == NULL) {
258         if (verbose >= 1) fprintf(stderr, "open greetings [%s] failed\n", gree);
259         return 0;
260     }
261     while ((ret = fread(bf, 1, 512, fp)) > 0) {
262         for (i = 0, e = 0 ; i < ret ; i++, e++) {
263             if (bf[i] == '\x10') {
264                 bf2[e++] = '\x10';
265                 bf2[e]   = '\x10';
266                 if (verbose >= 5) fprintf(stderr, "bf[%u] bf[%u]\n", bf[i], bf[i+1]);
267             }
268             else {
269                 bf2[e] = bf[i];
270             }
271         }
272         write(fh, bf2, e);
273     }
274     fclose(fp);
275
276     return 1;
277 }
278
279 /*
280  *  MAIN
281  */
282 #define BAUDRATE         B115200
283 #define RECORD_LEN       60
284 #define RING_NUMB        5
285 #define RING_INTER       8
286 #define GREETINGS        "/var/spool/mopice/messages/greetings.raw"
287 #define OUTPATH          "/var/spool/mopice/incoming"
288 #define SDEVICE          "/dev/ttyS0"
289 #define OUTFILE_FORMAT   "%Y%m%d-%H%M%S"
290 int main(int argc, char *argv[])
291 {
292     int fh;
293     FILE *fp;
294     struct termios oldtio,newtio;
295     struct sigaction sa;
296     int i, e, ret;
297     unsigned char bf[10240],  bf2[10240];
298     char outname[10240];
299     int ring_numb = RING_NUMB, ring_inter = RING_INTER;
300     struct timespec ts;
301     time_t tout;
302     int is_busy;
303
304     char outstr[200];
305     time_t t;
306     struct tm *tmp;
307
308     /* --- signal handling --- */
309     /* NOTE: without it using the more simple old school
310        signal()/alarm() the read syscall restart and not exit */
311     sa.sa_handler = alarm_sh;
312     sigemptyset(&sa.sa_mask);
313     sa.sa_flags = 0;
314     if (sigaction(SIGALRM, &sa, NULL))
315         exit(1);
316
317     if ((fh = open(SDEVICE, O_RDWR | O_NOCTTY)) == -1) {
318         if (verbose >= 1) fprintf(stderr, "tty open failed\n");
319         exit(1);
320     }
321
322     /* --- serial initialization --- */
323     ioctl( fh, TIOCNOTTY, 0 );
324
325     tcgetattr(fh,&oldtio); /* save current serial port settings */
326     bzero(&newtio, sizeof(newtio));
327     newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
328     newtio.c_iflag = IGNPAR;
329     newtio.c_oflag = 0;
330     
331     /* set input mode (non-canonical, no echo,...) */
332     newtio.c_lflag = 0;
333     
334     newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
335     newtio.c_cc[VMIN]     = 1;   /* blocking read until 5 chars received */
336     
337     tcflush(fh, TCIFLUSH);
338     tcsetattr(fh,TCSANOW,&newtio);
339     
340
341     /* --- modem initialization --- */    
342     ret = send_cmds(fh, init_cmds, sizeof(init_cmds) / sizeof(struct cmd));
343     if (verbose >= 1) fprintf(stderr, "INIT_CMDS: %d\n", ret);
344     if (ret == 0) {
345         exit (2);
346     }
347     while (1) {
348         /* wait the right number of rings */
349 #if 1
350         if (ring_waiter(fh, ring_numb, ring_inter) == 0) {
351             if (verbose >= 1) fprintf(stderr, "RING WAITER failed\n");
352             exit (2);
353         }
354 #else
355         ring_numb = fgetc(stdin);
356 #endif
357
358         /* instruct modem for greetings message */
359         ret = send_cmds(fh, answ_cmds, sizeof(answ_cmds) / sizeof(struct cmd));
360         if (verbose >= 1) fprintf(stderr, "ANSW_CMDS: %d\n", ret);
361         if (ret == 0) {
362             exit(4);
363         }
364
365         /* send the greetings message */
366         /* NOTE: sox greetings.wav -e unsigned-integer -b 8 -r 8000 -c 1 greetings.raw */
367         if (greetings_send(fh, GREETINGS) == 0) {
368             exit(5);
369         }
370
371         /* instruct modem to send a beep and start message recording */
372         ret = send_cmds(fh, recmsg_cmds, sizeof(recmsg_cmds) / sizeof(struct cmd));
373         if (verbose >= 1) fprintf(stderr, "RECMSG_CMDS: %d\n", ret);
374
375         /* record the message */
376         t = time(NULL);
377         tmp = localtime(&t);
378         if (tmp == NULL) {
379             if (verbose >= 1) fprintf(stderr, "localtime failed\n");
380             exit(6);
381         }
382         if (strftime(outstr, sizeof(outstr), OUTFILE_FORMAT, tmp) == 0) {
383             if (verbose >= 1) fprintf(stderr, "strftime failed\n");
384             exit(7);
385         }
386         sprintf(outname, "%s/%s.dli", OUTPATH, outstr);
387         if ((fp = fopen(outname, "w")) == NULL) {
388             exit(8);
389         }
390         clock_gettime(CLOCK_MONOTONIC, &ts);
391         tout = ts.tv_sec + RECORD_LEN;
392         
393         is_busy = 0;
394         while (is_busy == 0 && (ret = read(fh, bf, 4096)) > 0) {
395             clock_gettime(CLOCK_MONOTONIC, &ts);
396             if (ts.tv_sec >= tout) {
397                 if (verbose >= 3) fprintf(stderr, "read_reply::end1\n");
398                 break;
399             }
400
401             for (i = 0, e = 0 ; i < ret ; i++, e++) {
402                 if (bf[i] == '\x10') {
403                     i++;
404                     if (verbose >= 1) fprintf(stderr, "0x10 found followed by %d\n", bf[i]);
405                     if (bf[i] == 'b') {
406                         if (verbose >= 1) fprintf(stderr, "BUSY found, stop recording\n");
407                         is_busy = 1;
408                         break;
409                     }
410                 }
411                 bf2[e] = bf[i];
412             }
413             
414             fwrite(bf2, 1, e, fp);
415             fflush(fp);
416         }
417         fclose(fp);
418         
419         /* reset the modem for the next call */
420         ret = send_cmds(fh, clo_cmds, sizeof(clo_cmds) / sizeof(struct cmd));
421         if (verbose >= 1) fprintf(stderr, "CLO_CMDS: %d\n", ret);
422     }
423
424     /* reset the serial to the old configs */
425     
426     tcsetattr(fh, TCSANOW, &oldtio);
427
428     close(fh);
429
430     exit (0);
431     return (0);
432 }