Add: g_at_chat_cancel_all function
[platform/upstream/connman.git] / gatchat / gatchat.c
1 /*
2  *
3  *  AT chat library with GLib integration
4  *
5  *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include <glib.h>
34
35 #include "ringbuffer.h"
36 #include "gatchat.h"
37
38 /* #define WRITE_SCHEDULER_DEBUG 1 */
39
40 static const char *none_prefix[] = { NULL };
41
42 static void g_at_chat_wakeup_writer(GAtChat *chat);
43 static void debug_chat(GAtChat *chat, gboolean in, const char *str, gsize len);
44
45 struct at_command {
46         char *cmd;
47         char **prefixes;
48         gboolean expect_pdu;
49         guint id;
50         GAtResultFunc callback;
51         GAtNotifyFunc listing;
52         gpointer user_data;
53         GDestroyNotify notify;
54 };
55
56 struct at_notify_node {
57         guint id;
58         GAtNotifyFunc callback;
59         gpointer user_data;
60         GDestroyNotify notify;
61 };
62
63 struct at_notify {
64         GSList *nodes;
65         gboolean pdu;
66 };
67
68 struct _GAtChat {
69         gint ref_count;                         /* Ref count */
70         guint next_cmd_id;                      /* Next command id */
71         guint next_notify_id;                   /* Next notify id */
72         guint read_watch;                       /* GSource read id, 0 if none */
73         guint write_watch;                      /* GSource write id, 0 if none */
74         GIOChannel *channel;                    /* channel */
75         GQueue *command_queue;                  /* Command queue */
76         guint cmd_bytes_written;                /* bytes written from cmd */
77         GHashTable *notify_list;                /* List of notification reg */
78         GAtDisconnectFunc user_disconnect;      /* user disconnect func */
79         gpointer user_disconnect_data;          /* user disconnect data */
80         struct ring_buffer *buf;                /* Current read buffer */
81         guint read_so_far;                      /* Number of bytes processed */
82         GAtDebugFunc debugf;                    /* debugging output function */
83         gpointer debug_data;                    /* Data to pass to debug func */
84         char *pdu_notify;                       /* Unsolicited Resp w/ PDU */
85         GSList *response_lines;                 /* char * lines of the response */
86         char *wakeup;                           /* command sent to wakeup modem */
87         gint timeout_source;
88         gdouble inactivity_time;                /* Period of inactivity */
89         guint wakeup_timeout;                   /* How long to wait for resp */
90         GTimer *wakeup_timer;                   /* Keep track of elapsed time */
91         GAtSyntax *syntax;
92         gboolean destroyed;                     /* Re-entrancy guard */
93         GSList *terminator_list;                /* Non-standard terminator */
94 };
95
96 struct terminator_info {
97         char *terminator;
98         int len;
99         gboolean success;
100 };
101
102 static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
103 {
104         const struct at_notify_node *node = a;
105         guint id = GPOINTER_TO_UINT(b);
106
107         if (node->id < id)
108                 return -1;
109
110         if (node->id > id)
111                 return 1;
112
113         return 0;
114 }
115
116 static void at_notify_node_destroy(struct at_notify_node *node)
117 {
118         if (node->notify)
119                 node->notify(node->user_data);
120
121         g_free(node);
122 }
123
124 static void at_notify_destroy(struct at_notify *notify)
125 {
126         g_slist_foreach(notify->nodes, (GFunc) at_notify_node_destroy, NULL);
127         g_free(notify);
128 }
129
130 static gint at_command_compare_by_id(gconstpointer a, gconstpointer b)
131 {
132         const struct at_command *command = a;
133         guint id = GPOINTER_TO_UINT(b);
134
135         if (command->id < id)
136                 return -1;
137
138         if (command->id > id)
139                 return 1;
140
141         return 0;
142 }
143
144 static struct at_command *at_command_create(const char *cmd,
145                                                 const char **prefix_list,
146                                                 gboolean expect_pdu,
147                                                 GAtNotifyFunc listing,
148                                                 GAtResultFunc func,
149                                                 gpointer user_data,
150                                                 GDestroyNotify notify,
151                                                 gboolean wakeup)
152 {
153         struct at_command *c;
154         gsize len;
155         char **prefixes = NULL;
156
157         if (prefix_list) {
158                 int num_prefixes = 0;
159                 int i;
160
161                 while (prefix_list[num_prefixes])
162                         num_prefixes += 1;
163
164                 prefixes = g_new(char *, num_prefixes + 1);
165
166                 for (i = 0; i < num_prefixes; i++)
167                         prefixes[i] = strdup(prefix_list[i]);
168
169                 prefixes[num_prefixes] = NULL;
170         }
171
172         c = g_try_new0(struct at_command, 1);
173
174         if (!c)
175                 return 0;
176
177         len = strlen(cmd);
178         c->cmd = g_try_new(char, len + 2);
179
180         if (!c->cmd) {
181                 g_free(c);
182                 return 0;
183         }
184
185         memcpy(c->cmd, cmd, len);
186
187         /* If we have embedded '\r' then this is a command expecting a prompt
188          * from the modem.  Embed Ctrl-Z at the very end automatically
189          */
190         if (wakeup == FALSE) {
191                 if (strchr(cmd, '\r'))
192                         c->cmd[len] = 26;
193                 else
194                         c->cmd[len] = '\r';
195
196                 len += 1;
197         }
198
199         c->cmd[len] = '\0';
200
201         c->expect_pdu = expect_pdu;
202         c->prefixes = prefixes;
203         c->callback = func;
204         c->listing = listing;
205         c->user_data = user_data;
206         c->notify = notify;
207
208         return c;
209 }
210
211 static void at_command_destroy(struct at_command *cmd)
212 {
213         if (cmd->notify)
214                 cmd->notify(cmd->user_data);
215
216         g_strfreev(cmd->prefixes);
217         g_free(cmd->cmd);
218         g_free(cmd);
219 }
220
221 static void free_terminator(struct terminator_info *info)
222 {
223         g_free(info->terminator);
224         info->terminator = NULL;
225         g_free(info);
226         info = NULL;
227 }
228
229 static void g_at_chat_cleanup(GAtChat *chat)
230 {
231         struct at_command *c;
232
233         ring_buffer_free(chat->buf);
234         chat->buf = NULL;
235
236         /* Cleanup pending commands */
237         while ((c = g_queue_pop_head(chat->command_queue)))
238                 at_command_destroy(c);
239
240         g_queue_free(chat->command_queue);
241         chat->command_queue = NULL;
242
243         /* Cleanup any response lines we have pending */
244         g_slist_foreach(chat->response_lines, (GFunc)g_free, NULL);
245         g_slist_free(chat->response_lines);
246         chat->response_lines = NULL;
247
248         /* Cleanup registered notifications */
249         g_hash_table_destroy(chat->notify_list);
250         chat->notify_list = NULL;
251
252         if (chat->pdu_notify) {
253                 g_free(chat->pdu_notify);
254                 chat->pdu_notify = NULL;
255         }
256
257         if (chat->wakeup) {
258                 g_free(chat->wakeup);
259                 chat->wakeup = NULL;
260         }
261
262         if (chat->wakeup_timer) {
263                 g_timer_destroy(chat->wakeup_timer);
264                 chat->wakeup_timer = 0;
265         }
266
267         if (chat->timeout_source) {
268                 g_source_remove(chat->timeout_source);
269                 chat->timeout_source = 0;
270         }
271
272         g_at_syntax_unref(chat->syntax);
273         chat->syntax = NULL;
274
275         chat->channel = NULL;
276
277         if (chat->terminator_list) {
278                 g_slist_foreach(chat->terminator_list,
279                                         (GFunc)free_terminator, NULL);
280                 g_slist_free(chat->terminator_list);
281                 chat->terminator_list = NULL;
282         }
283 }
284
285 static void read_watcher_destroy_notify(GAtChat *chat)
286 {
287         g_at_chat_cleanup(chat);
288         chat->read_watch = 0;
289
290         if (chat->user_disconnect)
291                 chat->user_disconnect(chat->user_disconnect_data);
292
293         if (chat->destroyed)
294                 g_free(chat);
295 }
296
297 static void write_watcher_destroy_notify(GAtChat *chat)
298 {
299         chat->write_watch = 0;
300 }
301
302 static void at_notify_call_callback(gpointer data, gpointer user_data)
303 {
304         struct at_notify_node *node = data;
305         GAtResult *result = user_data;
306
307         node->callback(result, node->user_data);
308 }
309
310 static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
311 {
312         GHashTableIter iter;
313         struct at_notify *notify;
314         char *prefix;
315         gpointer key, value;
316         gboolean ret = FALSE;
317         GAtResult result;
318
319         g_hash_table_iter_init(&iter, chat->notify_list);
320         result.lines = 0;
321         result.final_or_pdu = 0;
322
323         while (g_hash_table_iter_next(&iter, &key, &value)) {
324                 prefix = key;
325                 notify = value;
326
327                 if (!g_str_has_prefix(line, key))
328                         continue;
329
330                 if (notify->pdu) {
331                         chat->pdu_notify = line;
332
333                         if (chat->syntax->set_hint)
334                                 chat->syntax->set_hint(chat->syntax,
335                                                         G_AT_SYNTAX_EXPECT_PDU);
336                         return TRUE;
337                 }
338
339                 if (!result.lines)
340                         result.lines = g_slist_prepend(NULL, line);
341
342                 g_slist_foreach(notify->nodes, at_notify_call_callback,
343                                         &result);
344                 ret = TRUE;
345         }
346
347         if (ret) {
348                 g_slist_free(result.lines);
349                 g_free(line);
350         }
351
352         return ret;
353 }
354
355 static void g_at_chat_finish_command(GAtChat *p, gboolean ok,
356                                                 char *final)
357 {
358         struct at_command *cmd = g_queue_pop_head(p->command_queue);
359         GSList *response_lines;
360
361         /* Cannot happen, but lets be paranoid */
362         if (!cmd)
363                 return;
364
365         p->cmd_bytes_written = 0;
366
367         if (g_queue_peek_head(p->command_queue))
368                 g_at_chat_wakeup_writer(p);
369
370         response_lines = p->response_lines;
371         p->response_lines = NULL;
372
373         if (cmd->callback) {
374                 GAtResult result;
375
376                 response_lines = g_slist_reverse(response_lines);
377
378                 result.final_or_pdu = final;
379                 result.lines = response_lines;
380
381                 cmd->callback(ok, &result, cmd->user_data);
382         }
383
384         g_slist_foreach(response_lines, (GFunc)g_free, NULL);
385         g_slist_free(response_lines);
386
387         g_free(final);
388         at_command_destroy(cmd);
389 }
390
391 static struct terminator_info terminator_table[] = {
392         { "OK", -1, TRUE },
393         { "ERROR", -1, FALSE },
394         { "NO DIALTONE", -1, FALSE },
395         { "BUSY", -1, FALSE },
396         { "NO CARRIER", -1, FALSE },
397         { "CONNECT", -1, TRUE },
398         { "NO ANSWER", -1, FALSE },
399         { "+CMS ERROR:", 11, FALSE },
400         { "+CME ERROR:", 11, FALSE },
401         { "+EXT ERROR:", 11, FALSE }
402 };
403
404 void g_at_chat_add_terminator(GAtChat *chat, char *terminator,
405                                         int len, gboolean success)
406 {
407         struct terminator_info *info = g_new0(struct terminator_info, 1);
408         info->terminator = g_strdup(terminator);
409         info->len = len;
410         info->success = success;
411         chat->terminator_list = g_slist_prepend(chat->terminator_list, info);
412 }
413
414 static gboolean check_terminator(struct terminator_info *info, char *line)
415 {
416         if (info->len == -1 && !strcmp(line, info->terminator))
417                 return TRUE;
418
419         if (info->len > 0 && !strncmp(line, info->terminator, info->len))
420                 return TRUE;
421
422         return FALSE;
423 }
424
425 static gboolean g_at_chat_handle_command_response(GAtChat *p,
426                                                         struct at_command *cmd,
427                                                         char *line)
428 {
429         int i;
430         int size = sizeof(terminator_table) / sizeof(struct terminator_info);
431         int hint;
432         GSList *l;
433
434         for (i = 0; i < size; i++) {
435                 struct terminator_info *info = &terminator_table[i];
436                 if (check_terminator(info, line)) {
437                         g_at_chat_finish_command(p, info->success, line);
438                         return TRUE;
439                 }
440         }
441
442         for (l = p->terminator_list; l; l = l->next) {
443                 struct terminator_info *info = l->data;
444                 if (check_terminator(info, line)) {
445                         g_at_chat_finish_command(p, info->success, line);
446                         return TRUE;
447                 }
448         }
449
450         if (cmd->prefixes) {
451                 int i;
452
453                 for (i = 0; cmd->prefixes[i]; i++)
454                         if (g_str_has_prefix(line, cmd->prefixes[i]))
455                                 goto out;
456
457                 return FALSE;
458         }
459
460 out:
461         if (cmd->listing && cmd->expect_pdu)
462                 hint = G_AT_SYNTAX_EXPECT_PDU;
463         else
464                 hint = G_AT_SYNTAX_EXPECT_MULTILINE;
465
466         if (p->syntax->set_hint)
467                 p->syntax->set_hint(p->syntax, hint);
468
469         if (cmd->listing && cmd->expect_pdu) {
470                 p->pdu_notify = line;
471                 return TRUE;
472         }
473
474         if (cmd->listing) {
475                 GAtResult result;
476
477                 result.lines = g_slist_prepend(NULL, line);
478                 result.final_or_pdu = NULL;
479
480                 cmd->listing(&result, cmd->user_data);
481
482                 g_slist_free(result.lines);
483                 g_free(line);
484         } else
485                 p->response_lines = g_slist_prepend(p->response_lines, line);
486
487         return TRUE;
488 }
489
490 static void have_line(GAtChat *p, char *str)
491 {
492         /* We're not going to copy terminal <CR><LF> */
493         struct at_command *cmd;
494
495         if (!str)
496                 return;
497
498         /* Check for echo, this should not happen, but lets be paranoid */
499         if (!strncmp(str, "AT", 2) == TRUE)
500                 goto done;
501
502         cmd = g_queue_peek_head(p->command_queue);
503
504         if (cmd && p->cmd_bytes_written > 0) {
505                 char c = cmd->cmd[p->cmd_bytes_written - 1];
506
507                 /* We check that we have submitted a terminator, in which case
508                  * a command might have failed or completed successfully
509                  *
510                  * In the generic case, \r is at the end of the command, so we
511                  * know the entire command has been submitted.  In the case of
512                  * commands like CMGS, every \r or Ctrl-Z might result in a
513                  * final response from the modem, so we check this as well.
514                  */
515                 if ((c == '\r' || c == 26) &&
516                                 g_at_chat_handle_command_response(p, cmd, str))
517                         return;
518         }
519
520         if (g_at_chat_match_notify(p, str) == TRUE)
521                 return;
522
523 done:
524         /* No matches & no commands active, ignore line */
525         g_free(str);
526 }
527
528 static void have_notify_pdu(GAtChat *p, char *pdu, GAtResult *result)
529 {
530         GHashTableIter iter;
531         struct at_notify *notify;
532         char *prefix;
533         gpointer key, value;
534
535         g_hash_table_iter_init(&iter, p->notify_list);
536
537         while (g_hash_table_iter_next(&iter, &key, &value)) {
538                 prefix = key;
539                 notify = value;
540
541                 if (!g_str_has_prefix(p->pdu_notify, prefix))
542                         continue;
543
544                 if (!notify->pdu)
545                         continue;
546
547                 g_slist_foreach(notify->nodes, at_notify_call_callback, result);
548         }
549 }
550
551 static void have_pdu(GAtChat *p, char *pdu)
552 {
553         struct at_command *cmd;
554         GAtResult result;
555         gboolean listing_pdu = FALSE;
556
557         if (!pdu)
558                 goto err;
559
560         result.lines = g_slist_prepend(NULL, p->pdu_notify);
561         result.final_or_pdu = pdu;
562
563         cmd = g_queue_peek_head(p->command_queue);
564
565         if (cmd && cmd->expect_pdu && p->cmd_bytes_written > 0) {
566                 char c = cmd->cmd[p->cmd_bytes_written - 1];
567
568                 if (c == '\r')
569                         listing_pdu = TRUE;
570         }
571
572         if (listing_pdu) {
573                 cmd->listing(&result, cmd->user_data);
574
575                 if (p->syntax->set_hint)
576                         p->syntax->set_hint(p->syntax,
577                                                 G_AT_SYNTAX_EXPECT_MULTILINE);
578         } else
579                 have_notify_pdu(p, pdu, &result);
580
581         g_slist_free(result.lines);
582
583 err:
584         g_free(p->pdu_notify);
585         p->pdu_notify = NULL;
586
587         if (pdu)
588                 g_free(pdu);
589 }
590
591 static char *extract_line(GAtChat *p)
592 {
593         unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
594         unsigned int pos = 0;
595         unsigned char *buf = ring_buffer_read_ptr(p->buf, pos);
596         int strip_front = 0;
597         int line_length = 0;
598         char *line;
599
600         while (pos < p->read_so_far) {
601                 if (*buf == '\r' || *buf == '\n')
602                         if (!line_length)
603                                 strip_front += 1;
604                         else
605                                 break;
606                 else
607                         line_length += 1;
608
609                 buf += 1;
610                 pos += 1;
611
612                 if (pos == wrap)
613                         buf = ring_buffer_read_ptr(p->buf, pos);
614         }
615
616         line = g_try_new(char, line_length + 1);
617
618         if (!line) {
619                 ring_buffer_drain(p->buf, p->read_so_far);
620                 return NULL;
621         }
622
623         ring_buffer_drain(p->buf, strip_front);
624         ring_buffer_read(p->buf, line, line_length);
625         ring_buffer_drain(p->buf, p->read_so_far - strip_front - line_length);
626
627         line[line_length] = '\0';
628
629         return line;
630 }
631
632 static void new_bytes(GAtChat *p)
633 {
634         unsigned int len = ring_buffer_len(p->buf);
635         unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
636         unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
637
638         GAtSyntaxResult result;
639
640         g_at_chat_ref(p);
641
642         while (p->channel && (p->read_so_far < len)) {
643                 gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
644                 result = p->syntax->feed(p->syntax, (char *)buf, &rbytes);
645
646                 buf += rbytes;
647                 p->read_so_far += rbytes;
648
649                 if (p->read_so_far == wrap) {
650                         buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
651                         wrap = len;
652                 }
653
654                 if (result == G_AT_SYNTAX_RESULT_UNSURE)
655                         continue;
656
657                 switch (result) {
658                 case G_AT_SYNTAX_RESULT_LINE:
659                 case G_AT_SYNTAX_RESULT_MULTILINE:
660                         have_line(p, extract_line(p));
661                         break;
662
663                 case G_AT_SYNTAX_RESULT_PDU:
664                         have_pdu(p, extract_line(p));
665                         break;
666
667                 case G_AT_SYNTAX_RESULT_PROMPT:
668                         g_at_chat_wakeup_writer(p);
669                         ring_buffer_drain(p->buf, p->read_so_far);
670                         break;
671
672                 default:
673                         ring_buffer_drain(p->buf, p->read_so_far);
674                         break;
675                 }
676
677                 len -= p->read_so_far;
678                 wrap -= p->read_so_far;
679                 p->read_so_far = 0;
680         }
681
682         /* We're overflowing the buffer, shutdown the socket */
683         if (p->buf && ring_buffer_avail(p->buf) == 0)
684                 g_source_remove(p->read_watch);
685
686         g_at_chat_unref(p);
687 }
688
689 static void debug_chat(GAtChat *chat, gboolean in, const char *str, gsize len)
690 {
691         char type = in ? '<' : '>';
692         gsize escaped = 2; /* Enough for '<', ' ' */
693         char *escaped_str;
694         const char *esc = "<ESC>";
695         gsize esc_size = strlen(esc);
696         const char *ctrlz = "<CtrlZ>";
697         gsize ctrlz_size = strlen(ctrlz);
698         gsize i;
699
700         if (!chat->debugf || !len)
701                 return;
702
703         for (i = 0; i < len; i++) {
704                 char c = str[i];
705
706                 if (isprint(c))
707                         escaped += 1;
708                 else if (c == '\r' || c == '\t' || c == '\n')
709                         escaped += 2;
710                 else if (c == 26)
711                         escaped += ctrlz_size;
712                 else if (c == 25)
713                         escaped += esc_size;
714                 else
715                         escaped += 4;
716         }
717
718         escaped_str = g_malloc(escaped + 1);
719         escaped_str[0] = type;
720         escaped_str[1] = ' ';
721         escaped_str[2] = '\0';
722         escaped_str[escaped] = '\0';
723
724         for (escaped = 2, i = 0; i < len; i++) {
725                 char c = str[i];
726
727                 switch (c) {
728                 case '\r':
729                         escaped_str[escaped++] = '\\';
730                         escaped_str[escaped++] = 'r';
731                         break;
732                 case '\t':
733                         escaped_str[escaped++] = '\\';
734                         escaped_str[escaped++] = 't';
735                         break;
736                 case '\n':
737                         escaped_str[escaped++] = '\\';
738                         escaped_str[escaped++] = 'n';
739                         break;
740                 case 26:
741                         strncpy(&escaped_str[escaped], ctrlz, ctrlz_size);
742                         escaped += ctrlz_size;
743                         break;
744                 case 25:
745                         strncpy(&escaped_str[escaped], esc, esc_size);
746                         escaped += esc_size;
747                         break;
748                 default:
749                         if (isprint(c))
750                                 escaped_str[escaped++] = c;
751                         else {
752                                 escaped_str[escaped++] = '\\';
753                                 escaped_str[escaped++] = '0' + ((c >> 6) & 07);
754                                 escaped_str[escaped++] = '0' + ((c >> 3) & 07);
755                                 escaped_str[escaped++] = '0' + (c & 07);
756                         }
757                 }
758         }
759
760         chat->debugf(escaped_str, chat->debug_data);
761         g_free(escaped_str);
762 }
763
764 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
765                                 gpointer data)
766 {
767         unsigned char *buf;
768         GAtChat *chat = data;
769         GIOError err;
770         gsize rbytes;
771         gsize toread;
772         gsize total_read = 0;
773
774         if (cond & G_IO_NVAL)
775                 return FALSE;
776
777         /* Regardless of condition, try to read all the data available */
778         do {
779                 rbytes = 0;
780
781                 toread = ring_buffer_avail_no_wrap(chat->buf);
782
783                 if (toread == 0)
784                         break;
785
786                 buf = ring_buffer_write_ptr(chat->buf);
787
788                 err = g_io_channel_read(channel, (char *) buf, toread, &rbytes);
789                 debug_chat(chat, TRUE, (char *)buf, rbytes);
790
791                 total_read += rbytes;
792
793                 if (rbytes > 0)
794                         ring_buffer_write_advance(chat->buf, rbytes);
795
796         } while (err == G_IO_ERROR_NONE && rbytes > 0);
797
798         if (total_read > 0)
799                 new_bytes(chat);
800
801         if (cond & (G_IO_HUP | G_IO_ERR))
802                 return FALSE;
803
804         if (err != G_IO_ERROR_NONE && err != G_IO_ERROR_AGAIN)
805                 return FALSE;
806
807         return TRUE;
808 }
809
810 static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data)
811 {
812         GAtChat *chat = user_data;
813
814         if (ok == FALSE)
815                 return;
816
817         if (chat->debugf)
818                 chat->debugf("Finally woke up the modem\n", chat->debug_data);
819
820         g_source_remove(chat->timeout_source);
821         chat->timeout_source = 0;
822 }
823
824 static gboolean wakeup_no_response(gpointer user)
825 {
826         GAtChat *chat = user;
827         struct at_command *cmd = g_queue_peek_head(chat->command_queue);
828
829         if (chat->debugf)
830                 chat->debugf("Wakeup got no response\n", chat->debug_data);
831
832         g_at_chat_finish_command(chat, FALSE, NULL);
833         cmd = at_command_create(chat->wakeup, none_prefix, FALSE,
834                                 NULL, wakeup_cb, chat, NULL, TRUE);
835
836         if (!cmd) {
837                 chat->timeout_source = 0;
838                 return FALSE;
839         }
840
841         g_queue_push_head(chat->command_queue, cmd);
842
843         return TRUE;
844 }
845
846 static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
847                                 gpointer data)
848 {
849         GAtChat *chat = data;
850         struct at_command *cmd;
851         GIOError err;
852         gsize bytes_written;
853         gsize towrite;
854         gsize len;
855         char *cr;
856         gboolean wakeup_first = FALSE;
857 #ifdef WRITE_SCHEDULER_DEBUG
858         int limiter;
859 #endif
860
861         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
862                 return FALSE;
863
864         /* Grab the first command off the queue and write as
865          * much of it as we can
866          */
867         cmd = g_queue_peek_head(chat->command_queue);
868
869         /* For some reason command queue is empty, cancel write watcher */
870         if (cmd == NULL)
871                 return FALSE;
872
873         len = strlen(cmd->cmd);
874
875         /* For some reason write watcher fired, but we've already
876          * written the entire command out to the io channel,
877          * cancel write watcher
878          */
879         if (chat->cmd_bytes_written >= len)
880                 return FALSE;
881
882         if (chat->wakeup) {
883                 if (!chat->wakeup_timer) {
884                         wakeup_first = TRUE;
885                         chat->wakeup_timer = g_timer_new();
886
887                 } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
888                                 chat->inactivity_time)
889                         wakeup_first = TRUE;
890         }
891
892         if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) {
893                 cmd = at_command_create(chat->wakeup, none_prefix, FALSE,
894                                         NULL, wakeup_cb, chat, NULL, TRUE);
895
896                 if (!cmd)
897                         return FALSE;
898
899                 g_queue_push_head(chat->command_queue, cmd);
900
901                 len = strlen(chat->wakeup);
902
903                 chat->timeout_source = g_timeout_add(chat->wakeup_timeout,
904                                                 wakeup_no_response, chat);
905         }
906
907         towrite = len - chat->cmd_bytes_written;
908
909         cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r');
910
911         if (cr)
912                 towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1;
913
914 #ifdef WRITE_SCHEDULER_DEBUG
915         limiter = towrite;
916
917         if (limiter > 5)
918                 limiter = 5;
919 #endif
920
921         err = g_io_channel_write(chat->channel,
922                         cmd->cmd + chat->cmd_bytes_written,
923 #ifdef WRITE_SCHEDULER_DEBUG
924                         limiter,
925 #else
926                         towrite,
927 #endif
928                         &bytes_written);
929
930         if (err != G_IO_ERROR_NONE) {
931                 g_source_remove(chat->read_watch);
932                 return FALSE;
933         }
934
935         debug_chat(chat, FALSE, cmd->cmd + chat->cmd_bytes_written,
936                         bytes_written);
937         chat->cmd_bytes_written += bytes_written;
938
939         if (bytes_written < towrite)
940                 return TRUE;
941
942         /* Full command submitted, update timer */
943         if (chat->wakeup_timer)
944                 g_timer_start(chat->wakeup_timer);
945
946         return FALSE;
947 }
948
949 static void g_at_chat_wakeup_writer(GAtChat *chat)
950 {
951         if (chat->write_watch != 0)
952                 return;
953
954         chat->write_watch = g_io_add_watch_full(chat->channel,
955                                 G_PRIORITY_DEFAULT,
956                                 G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
957                                 can_write_data, chat,
958                                 (GDestroyNotify)write_watcher_destroy_notify);
959 }
960
961 GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax)
962 {
963         GAtChat *chat;
964         GIOFlags io_flags;
965
966         if (!channel)
967                 return NULL;
968
969         if (!syntax)
970                 return NULL;
971
972         chat = g_try_new0(GAtChat, 1);
973
974         if (!chat)
975                 return chat;
976
977         chat->ref_count = 1;
978         chat->next_cmd_id = 1;
979         chat->next_notify_id = 1;
980         chat->debugf = NULL;
981
982         chat->buf = ring_buffer_new(4096);
983
984         if (!chat->buf)
985                 goto error;
986
987         chat->command_queue = g_queue_new();
988
989         if (!chat->command_queue)
990                 goto error;
991
992         chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal,
993                                 g_free, (GDestroyNotify)at_notify_destroy);
994
995         if (g_io_channel_set_encoding(channel, NULL, NULL) !=
996                         G_IO_STATUS_NORMAL)
997                 goto error;
998
999         io_flags = g_io_channel_get_flags(channel);
1000
1001         io_flags |= G_IO_FLAG_NONBLOCK;
1002
1003         if (g_io_channel_set_flags(channel, io_flags, NULL) !=
1004                         G_IO_STATUS_NORMAL)
1005                 goto error;
1006
1007         g_io_channel_set_close_on_unref(channel, TRUE);
1008
1009         chat->channel = channel;
1010         chat->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
1011                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
1012                                 received_data, chat,
1013                                 (GDestroyNotify)read_watcher_destroy_notify);
1014
1015         chat->syntax = g_at_syntax_ref(syntax);
1016
1017         return chat;
1018
1019 error:
1020         if (chat->buf)
1021                 ring_buffer_free(chat->buf);
1022
1023         if (chat->command_queue)
1024                 g_queue_free(chat->command_queue);
1025
1026         if (chat->notify_list)
1027                 g_hash_table_destroy(chat->notify_list);
1028
1029         g_free(chat);
1030         return NULL;
1031 }
1032
1033 GIOChannel *g_at_chat_get_channel(GAtChat *chat)
1034 {
1035         if (chat == NULL)
1036                 return NULL;
1037
1038         return chat->channel;
1039 }
1040
1041 GAtChat *g_at_chat_ref(GAtChat *chat)
1042 {
1043         if (chat == NULL)
1044                 return NULL;
1045
1046         g_atomic_int_inc(&chat->ref_count);
1047
1048         return chat;
1049 }
1050
1051 void g_at_chat_unref(GAtChat *chat)
1052 {
1053         gboolean is_zero;
1054
1055         if (chat == NULL)
1056                 return;
1057
1058         is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
1059
1060         if (is_zero == FALSE)
1061                 return;
1062
1063         g_at_chat_shutdown(chat);
1064
1065         /* glib delays the destruction of the watcher until it exits, this
1066          * means we can't free the data just yet, even though we've been
1067          * destroyed already.  We have to wait until the read_watcher
1068          * destroy function gets called
1069          */
1070         if (chat->read_watch != 0)
1071                 chat->destroyed = TRUE;
1072         else
1073                 g_free(chat);
1074 }
1075
1076 gboolean g_at_chat_shutdown(GAtChat *chat)
1077 {
1078         if (chat->channel == NULL)
1079                 return FALSE;
1080
1081         /* Don't trigger user disconnect on shutdown */
1082         chat->user_disconnect = NULL;
1083         chat->user_disconnect_data = NULL;
1084
1085         if (chat->read_watch)
1086                 g_source_remove(chat->read_watch);
1087
1088         if (chat->write_watch)
1089                 g_source_remove(chat->write_watch);
1090
1091         return TRUE;
1092 }
1093
1094 gboolean g_at_chat_set_syntax(GAtChat *chat, GAtSyntax *syntax)
1095 {
1096         if (chat == NULL)
1097                 return FALSE;
1098
1099         g_at_syntax_unref(chat->syntax);
1100
1101         chat->syntax = g_at_syntax_ref(syntax);
1102
1103         return TRUE;
1104 }
1105
1106 gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
1107                         GAtDisconnectFunc disconnect, gpointer user_data)
1108 {
1109         if (chat == NULL)
1110                 return FALSE;
1111
1112         chat->user_disconnect = disconnect;
1113         chat->user_disconnect_data = user_data;
1114
1115         return TRUE;
1116 }
1117
1118 gboolean g_at_chat_set_debug(GAtChat *chat, GAtDebugFunc func, gpointer user)
1119 {
1120         if (chat == NULL)
1121                 return FALSE;
1122
1123         chat->debugf = func;
1124         chat->debug_data = user;
1125
1126         return TRUE;
1127 }
1128
1129 static guint send_common(GAtChat *chat, const char *cmd,
1130                         const char **prefix_list,
1131                         gboolean expect_pdu,
1132                         GAtNotifyFunc listing, GAtResultFunc func,
1133                         gpointer user_data, GDestroyNotify notify)
1134 {
1135         struct at_command *c;
1136
1137         if (chat == NULL || chat->command_queue == NULL)
1138                 return 0;
1139
1140         c = at_command_create(cmd, prefix_list, expect_pdu, listing, func,
1141                                 user_data, notify, FALSE);
1142
1143         if (!c)
1144                 return 0;
1145
1146         c->id = chat->next_cmd_id++;
1147
1148         g_queue_push_tail(chat->command_queue, c);
1149
1150         if (g_queue_get_length(chat->command_queue) == 1)
1151                 g_at_chat_wakeup_writer(chat);
1152
1153         return c->id;
1154 }
1155
1156 guint g_at_chat_send(GAtChat *chat, const char *cmd,
1157                         const char **prefix_list, GAtResultFunc func,
1158                         gpointer user_data, GDestroyNotify notify)
1159 {
1160         return send_common(chat, cmd, prefix_list, FALSE, NULL, func,
1161                                 user_data, notify);
1162 }
1163
1164 guint g_at_chat_send_listing(GAtChat *chat, const char *cmd,
1165                                 const char **prefix_list,
1166                                 GAtNotifyFunc listing, GAtResultFunc func,
1167                                 gpointer user_data, GDestroyNotify notify)
1168 {
1169         if (listing == NULL)
1170                 return 0;
1171
1172         return send_common(chat, cmd, prefix_list, FALSE, listing, func,
1173                                 user_data, notify);
1174 }
1175
1176 guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd,
1177                                 const char **prefix_list,
1178                                 GAtNotifyFunc listing, GAtResultFunc func,
1179                                 gpointer user_data, GDestroyNotify notify)
1180 {
1181         if (listing == NULL)
1182                 return 0;
1183
1184         return send_common(chat, cmd, prefix_list, TRUE, listing, func,
1185                                 user_data, notify);
1186 }
1187
1188 gboolean g_at_chat_cancel(GAtChat *chat, guint id)
1189 {
1190         GList *l;
1191
1192         if (chat == NULL || chat->command_queue == NULL)
1193                 return FALSE;
1194
1195         /* We use id 0 for wakeup commands */
1196         if (id == 0)
1197                 return FALSE;
1198
1199         l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
1200                                 at_command_compare_by_id);
1201
1202         if (!l)
1203                 return FALSE;
1204
1205         if (l == g_queue_peek_head(chat->command_queue) &&
1206                         chat->cmd_bytes_written > 0) {
1207                 struct at_command *c = l->data;
1208
1209                 /* We can't actually remove it since it is most likely
1210                  * already in progress, just null out the callback
1211                  * so it won't be called
1212                  */
1213                 c->callback = NULL;
1214         } else {
1215                 at_command_destroy(l->data);
1216                 g_queue_remove(chat->command_queue, l->data);
1217         }
1218
1219         return TRUE;
1220 }
1221
1222 gboolean g_at_chat_cancel_all(GAtChat *chat)
1223 {
1224         int n = 0;
1225         struct at_command *c;
1226
1227         if (chat == NULL || chat->command_queue == NULL)
1228                 return FALSE;
1229
1230         while ((c = g_queue_peek_nth(chat->command_queue, n)) != NULL) {
1231                 if (c->id == 0) {
1232                         n += 1;
1233                         continue;
1234                 }
1235
1236                 if (n == 0 && chat->cmd_bytes_written > 0) {
1237                         c->callback = NULL;
1238                         n += 1;
1239                         continue;
1240                 }
1241
1242                 at_command_destroy(c);
1243                 g_queue_remove(chat->command_queue, c);
1244         }
1245
1246         return TRUE;
1247 }
1248
1249 static struct at_notify *at_notify_create(GAtChat *chat, const char *prefix,
1250                                                 gboolean pdu)
1251 {
1252         struct at_notify *notify;
1253         char *key;
1254
1255         key = g_strdup(prefix);
1256
1257         if (!key)
1258                 return 0;
1259
1260         notify = g_try_new0(struct at_notify, 1);
1261
1262         if (!notify) {
1263                 g_free(key);
1264                 return 0;
1265         }
1266
1267         notify->pdu = pdu;
1268
1269         g_hash_table_insert(chat->notify_list, key, notify);
1270
1271         return notify;
1272 }
1273
1274 guint g_at_chat_register(GAtChat *chat, const char *prefix,
1275                                 GAtNotifyFunc func, gboolean expect_pdu,
1276                                 gpointer user_data,
1277                                 GDestroyNotify destroy_notify)
1278 {
1279         struct at_notify *notify;
1280         struct at_notify_node *node;
1281
1282         if (chat == NULL || chat->notify_list == NULL)
1283                 return 0;
1284
1285         if (func == NULL)
1286                 return 0;
1287
1288         if (prefix == NULL || strlen(prefix) == 0)
1289                 return 0;
1290
1291         notify = g_hash_table_lookup(chat->notify_list, prefix);
1292
1293         if (!notify)
1294                 notify = at_notify_create(chat, prefix, expect_pdu);
1295
1296         if (!notify || notify->pdu != expect_pdu)
1297                 return 0;
1298
1299         node = g_try_new0(struct at_notify_node, 1);
1300
1301         if (!node)
1302                 return 0;
1303
1304         node->id = chat->next_notify_id++;
1305         node->callback = func;
1306         node->user_data = user_data;
1307         node->notify = destroy_notify;
1308
1309         notify->nodes = g_slist_prepend(notify->nodes, node);
1310
1311         return node->id;
1312 }
1313
1314 gboolean g_at_chat_unregister(GAtChat *chat, guint id)
1315 {
1316         GHashTableIter iter;
1317         struct at_notify *notify;
1318         char *prefix;
1319         gpointer key, value;
1320         GSList *l;
1321
1322         if (chat == NULL || chat->notify_list == NULL)
1323                 return FALSE;
1324
1325         g_hash_table_iter_init(&iter, chat->notify_list);
1326
1327         while (g_hash_table_iter_next(&iter, &key, &value)) {
1328                 prefix = key;
1329                 notify = value;
1330
1331                 l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
1332                                         at_notify_node_compare_by_id);
1333
1334                 if (!l)
1335                         continue;
1336
1337                 at_notify_node_destroy(l->data);
1338                 notify->nodes = g_slist_remove(notify->nodes, l->data);
1339
1340                 if (notify->nodes == NULL)
1341                         g_hash_table_iter_remove(&iter);
1342
1343                 return TRUE;
1344         }
1345
1346         return TRUE;
1347 }
1348
1349 gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
1350                                         unsigned int timeout, unsigned int msec)
1351 {
1352         if (chat == NULL)
1353                 return FALSE;
1354
1355         if (chat->wakeup)
1356                 g_free(chat->wakeup);
1357
1358         chat->wakeup = g_strdup(cmd);
1359         chat->inactivity_time = (gdouble)msec / 1000;
1360         chat->wakeup_timeout = timeout;
1361
1362         return TRUE;
1363 }