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