3 * AT chat library with GLib integration
5 * Copyright (C) 2008-2010 Intel Corporation. All rights reserved.
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.
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.
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
35 #include "ringbuffer.h"
38 /* #define WRITE_SCHEDULER_DEBUG 1 */
40 static const char *none_prefix[] = { NULL };
42 static void g_at_chat_wakeup_writer(GAtChat *chat);
49 GAtResultFunc callback;
50 GAtNotifyFunc listing;
52 GDestroyNotify notify;
55 struct at_notify_node {
57 GAtNotifyFunc callback;
59 GDestroyNotify notify;
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 */
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 */
93 gboolean destroyed; /* Re-entrancy guard */
94 GSList *terminator_list; /* Non-standard terminator */
97 struct terminator_info {
103 static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
105 const struct at_notify_node *node = a;
106 guint id = GPOINTER_TO_UINT(b);
117 static void at_notify_node_destroy(struct at_notify_node *node)
120 node->notify(node->user_data);
125 static void at_notify_destroy(struct at_notify *notify)
127 g_slist_foreach(notify->nodes, (GFunc) at_notify_node_destroy, NULL);
131 static gint at_command_compare_by_id(gconstpointer a, gconstpointer b)
133 const struct at_command *command = a;
134 guint id = GPOINTER_TO_UINT(b);
136 if (command->id < id)
139 if (command->id > id)
145 static struct at_command *at_command_create(const char *cmd,
146 const char **prefix_list,
148 GAtNotifyFunc listing,
151 GDestroyNotify notify,
154 struct at_command *c;
156 char **prefixes = NULL;
159 int num_prefixes = 0;
162 while (prefix_list[num_prefixes])
165 prefixes = g_new(char *, num_prefixes + 1);
167 for (i = 0; i < num_prefixes; i++)
168 prefixes[i] = strdup(prefix_list[i]);
170 prefixes[num_prefixes] = NULL;
173 c = g_try_new0(struct at_command, 1);
179 c->cmd = g_try_new(char, len + 2);
186 memcpy(c->cmd, cmd, len);
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
191 if (wakeup == FALSE) {
192 if (strchr(cmd, '\r'))
202 c->expect_pdu = expect_pdu;
203 c->prefixes = prefixes;
205 c->listing = listing;
206 c->user_data = user_data;
212 static void at_command_destroy(struct at_command *cmd)
215 cmd->notify(cmd->user_data);
217 g_strfreev(cmd->prefixes);
222 static void free_terminator(struct terminator_info *info)
224 g_free(info->terminator);
225 info->terminator = NULL;
230 static void g_at_chat_cleanup(GAtChat *chat)
232 struct at_command *c;
234 ring_buffer_free(chat->buf);
237 /* Cleanup pending commands */
238 while ((c = g_queue_pop_head(chat->command_queue)))
239 at_command_destroy(c);
241 g_queue_free(chat->command_queue);
242 chat->command_queue = NULL;
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;
249 /* Cleanup registered notifications */
250 g_hash_table_destroy(chat->notify_list);
251 chat->notify_list = NULL;
253 if (chat->pdu_notify) {
254 g_free(chat->pdu_notify);
255 chat->pdu_notify = NULL;
259 g_free(chat->wakeup);
263 if (chat->wakeup_timer) {
264 g_timer_destroy(chat->wakeup_timer);
265 chat->wakeup_timer = 0;
268 if (chat->timeout_source) {
269 g_source_remove(chat->timeout_source);
270 chat->timeout_source = 0;
273 g_at_syntax_unref(chat->syntax);
276 chat->channel = NULL;
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;
286 static void read_watcher_destroy_notify(GAtChat *chat)
288 g_at_chat_cleanup(chat);
289 chat->read_watch = 0;
291 if (chat->user_disconnect)
292 chat->user_disconnect(chat->user_disconnect_data);
298 static void write_watcher_destroy_notify(GAtChat *chat)
300 chat->write_watch = 0;
303 static void at_notify_call_callback(gpointer data, gpointer user_data)
305 struct at_notify_node *node = data;
306 GAtResult *result = user_data;
308 node->callback(result, node->user_data);
311 static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
314 struct at_notify *notify;
317 gboolean ret = FALSE;
320 g_hash_table_iter_init(&iter, chat->notify_list);
322 result.final_or_pdu = 0;
324 while (g_hash_table_iter_next(&iter, &key, &value)) {
328 if (!g_str_has_prefix(line, key))
332 chat->pdu_notify = line;
334 if (chat->syntax->set_hint)
335 chat->syntax->set_hint(chat->syntax,
336 G_AT_SYNTAX_EXPECT_PDU);
341 result.lines = g_slist_prepend(NULL, line);
343 g_slist_foreach(notify->nodes, at_notify_call_callback,
349 g_slist_free(result.lines);
356 static void g_at_chat_finish_command(GAtChat *p, gboolean ok,
359 struct at_command *cmd = g_queue_pop_head(p->command_queue);
360 GSList *response_lines;
362 /* Cannot happen, but lets be paranoid */
366 p->cmd_bytes_written = 0;
368 if (g_queue_peek_head(p->command_queue))
369 g_at_chat_wakeup_writer(p);
371 response_lines = p->response_lines;
372 p->response_lines = NULL;
377 response_lines = g_slist_reverse(response_lines);
379 result.final_or_pdu = final;
380 result.lines = response_lines;
382 cmd->callback(ok, &result, cmd->user_data);
385 g_slist_foreach(response_lines, (GFunc)g_free, NULL);
386 g_slist_free(response_lines);
389 at_command_destroy(cmd);
392 static struct terminator_info terminator_table[] = {
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 }
405 void g_at_chat_add_terminator(GAtChat *chat, char *terminator,
406 int len, gboolean success)
408 struct terminator_info *info = g_new0(struct terminator_info, 1);
409 info->terminator = g_strdup(terminator);
411 info->success = success;
412 chat->terminator_list = g_slist_prepend(chat->terminator_list, info);
415 static gboolean check_terminator(struct terminator_info *info, char *line)
417 if (info->len == -1 && !strcmp(line, info->terminator))
420 if (info->len > 0 && !strncmp(line, info->terminator, info->len))
426 static gboolean g_at_chat_handle_command_response(GAtChat *p,
427 struct at_command *cmd,
431 int size = sizeof(terminator_table) / sizeof(struct terminator_info);
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);
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);
454 for (i = 0; cmd->prefixes[i]; i++)
455 if (g_str_has_prefix(line, cmd->prefixes[i]))
462 if (cmd->listing && cmd->expect_pdu)
463 hint = G_AT_SYNTAX_EXPECT_PDU;
465 hint = G_AT_SYNTAX_EXPECT_MULTILINE;
467 if (p->syntax->set_hint)
468 p->syntax->set_hint(p->syntax, hint);
470 if (cmd->listing && cmd->expect_pdu) {
471 p->pdu_notify = line;
478 result.lines = g_slist_prepend(NULL, line);
479 result.final_or_pdu = NULL;
481 cmd->listing(&result, cmd->user_data);
483 g_slist_free(result.lines);
486 p->response_lines = g_slist_prepend(p->response_lines, line);
491 static void have_line(GAtChat *p, char *str)
493 /* We're not going to copy terminal <CR><LF> */
494 struct at_command *cmd;
499 /* Check for echo, this should not happen, but lets be paranoid */
500 if (!strncmp(str, "AT", 2) == TRUE)
503 cmd = g_queue_peek_head(p->command_queue);
505 if (cmd && p->cmd_bytes_written > 0) {
506 char c = cmd->cmd[p->cmd_bytes_written - 1];
508 /* We check that we have submitted a terminator, in which case
509 * a command might have failed or completed successfully
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.
516 if ((c == '\r' || c == 26) &&
517 g_at_chat_handle_command_response(p, cmd, str))
521 if (g_at_chat_match_notify(p, str) == TRUE)
525 /* No matches & no commands active, ignore line */
529 static void have_notify_pdu(GAtChat *p, char *pdu, GAtResult *result)
532 struct at_notify *notify;
536 g_hash_table_iter_init(&iter, p->notify_list);
538 while (g_hash_table_iter_next(&iter, &key, &value)) {
542 if (!g_str_has_prefix(p->pdu_notify, prefix))
548 g_slist_foreach(notify->nodes, at_notify_call_callback, result);
552 static void have_pdu(GAtChat *p, char *pdu)
554 struct at_command *cmd;
556 gboolean listing_pdu = FALSE;
561 result.lines = g_slist_prepend(NULL, p->pdu_notify);
562 result.final_or_pdu = pdu;
564 cmd = g_queue_peek_head(p->command_queue);
566 if (cmd && cmd->expect_pdu && p->cmd_bytes_written > 0) {
567 char c = cmd->cmd[p->cmd_bytes_written - 1];
574 cmd->listing(&result, cmd->user_data);
576 if (p->syntax->set_hint)
577 p->syntax->set_hint(p->syntax,
578 G_AT_SYNTAX_EXPECT_MULTILINE);
580 have_notify_pdu(p, pdu, &result);
582 g_slist_free(result.lines);
585 g_free(p->pdu_notify);
586 p->pdu_notify = NULL;
592 static char *extract_line(GAtChat *p)
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);
601 while (pos < p->read_so_far) {
602 if (*buf == '\r' || *buf == '\n')
614 buf = ring_buffer_read_ptr(p->buf, pos);
617 line = g_try_new(char, line_length + 1);
620 ring_buffer_drain(p->buf, p->read_so_far);
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);
628 line[line_length] = '\0';
633 static void new_bytes(GAtChat *p)
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);
639 GAtSyntaxResult result;
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);
648 p->read_so_far += rbytes;
650 if (p->read_so_far == wrap) {
651 buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
655 if (result == G_AT_SYNTAX_RESULT_UNSURE)
659 case G_AT_SYNTAX_RESULT_LINE:
660 case G_AT_SYNTAX_RESULT_MULTILINE:
661 have_line(p, extract_line(p));
664 case G_AT_SYNTAX_RESULT_PDU:
665 have_pdu(p, extract_line(p));
668 case G_AT_SYNTAX_RESULT_PROMPT:
669 g_at_chat_wakeup_writer(p);
670 ring_buffer_drain(p->buf, p->read_so_far);
674 ring_buffer_drain(p->buf, p->read_so_far);
678 len -= p->read_so_far;
679 wrap -= p->read_so_far;
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);
690 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
694 GAtChat *chat = data;
698 gsize total_read = 0;
699 guint read_count = 0;
701 if (cond & G_IO_NVAL)
704 /* Regardless of condition, try to read all the data available */
706 toread = ring_buffer_avail_no_wrap(chat->buf);
712 buf = ring_buffer_write_ptr(chat->buf);
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);
720 total_read += rbytes;
723 ring_buffer_write_advance(chat->buf, rbytes);
725 } while (err == G_IO_ERROR_NONE && rbytes > 0 &&
726 read_count < chat->max_read_attempts);
731 if (cond & (G_IO_HUP | G_IO_ERR))
734 if (read_count > 0 && rbytes == 0 && err != G_IO_ERROR_AGAIN)
740 static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data)
742 GAtChat *chat = user_data;
748 chat->debugf("Finally woke up the modem\n", chat->debug_data);
750 g_source_remove(chat->timeout_source);
751 chat->timeout_source = 0;
754 static gboolean wakeup_no_response(gpointer user)
756 GAtChat *chat = user;
757 struct at_command *cmd = g_queue_peek_head(chat->command_queue);
760 chat->debugf("Wakeup got no response\n", chat->debug_data);
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);
767 chat->timeout_source = 0;
771 g_queue_push_head(chat->command_queue, cmd);
776 static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
779 GAtChat *chat = data;
780 struct at_command *cmd;
786 gboolean wakeup_first = FALSE;
787 #ifdef WRITE_SCHEDULER_DEBUG
791 if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
794 /* Grab the first command off the queue and write as
795 * much of it as we can
797 cmd = g_queue_peek_head(chat->command_queue);
799 /* For some reason command queue is empty, cancel write watcher */
803 len = strlen(cmd->cmd);
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
809 if (chat->cmd_bytes_written >= len)
813 if (!chat->wakeup_timer) {
815 chat->wakeup_timer = g_timer_new();
817 } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
818 chat->inactivity_time)
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);
829 g_queue_push_head(chat->command_queue, cmd);
831 len = strlen(chat->wakeup);
833 chat->timeout_source = g_timeout_add(chat->wakeup_timeout,
834 wakeup_no_response, chat);
837 towrite = len - chat->cmd_bytes_written;
839 cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r');
842 towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1;
844 #ifdef WRITE_SCHEDULER_DEBUG
851 err = g_io_channel_write(chat->channel,
852 cmd->cmd + chat->cmd_bytes_written,
853 #ifdef WRITE_SCHEDULER_DEBUG
860 if (err != G_IO_ERROR_NONE) {
861 g_source_remove(chat->read_watch);
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;
869 if (bytes_written < towrite)
872 /* Full command submitted, update timer */
873 if (chat->wakeup_timer)
874 g_timer_start(chat->wakeup_timer);
879 static void g_at_chat_wakeup_writer(GAtChat *chat)
881 if (chat->write_watch != 0)
884 if (chat->use_write_watch == TRUE) {
885 chat->write_watch = g_io_add_watch_full(chat->channel,
887 G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
888 can_write_data, chat,
889 (GDestroyNotify)write_watcher_destroy_notify);
891 while (can_write_data(chat->channel, G_IO_OUT, chat) == TRUE);
892 write_watcher_destroy_notify(chat);
896 static GAtChat *create_chat(GIOChannel *channel, GIOFlags flags,
907 chat = g_try_new0(GAtChat, 1);
913 chat->next_cmd_id = 1;
914 chat->next_notify_id = 1;
917 if (flags & G_IO_FLAG_NONBLOCK) {
918 chat->use_write_watch = TRUE;
919 chat->max_read_attempts = 3;
921 chat->use_write_watch = FALSE;
922 chat->max_read_attempts = 1;
925 chat->buf = ring_buffer_new(4096);
930 chat->command_queue = g_queue_new();
932 if (!chat->command_queue)
935 chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal,
936 g_free, (GDestroyNotify)at_notify_destroy);
938 if (!g_at_util_setup_io(channel, flags))
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,
945 (GDestroyNotify)read_watcher_destroy_notify);
947 chat->syntax = g_at_syntax_ref(syntax);
953 ring_buffer_free(chat->buf);
955 if (chat->command_queue)
956 g_queue_free(chat->command_queue);
958 if (chat->notify_list)
959 g_hash_table_destroy(chat->notify_list);
965 GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax)
967 return create_chat(channel, G_IO_FLAG_NONBLOCK, syntax);
970 GAtChat *g_at_chat_new_blocking(GIOChannel *channel, GAtSyntax *syntax)
972 return create_chat(channel, 0, syntax);
975 GIOChannel *g_at_chat_get_channel(GAtChat *chat)
980 return chat->channel;
983 GAtChat *g_at_chat_ref(GAtChat *chat)
988 g_atomic_int_inc(&chat->ref_count);
993 void g_at_chat_unref(GAtChat *chat)
1000 is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
1002 if (is_zero == FALSE)
1005 g_at_chat_shutdown(chat);
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
1012 if (chat->read_watch != 0)
1013 chat->destroyed = TRUE;
1018 gboolean g_at_chat_shutdown(GAtChat *chat)
1020 if (chat->channel == NULL)
1023 /* Don't trigger user disconnect on shutdown */
1024 chat->user_disconnect = NULL;
1025 chat->user_disconnect_data = NULL;
1027 if (chat->read_watch)
1028 g_source_remove(chat->read_watch);
1030 if (chat->write_watch)
1031 g_source_remove(chat->write_watch);
1036 gboolean g_at_chat_set_syntax(GAtChat *chat, GAtSyntax *syntax)
1041 g_at_syntax_unref(chat->syntax);
1043 chat->syntax = g_at_syntax_ref(syntax);
1048 gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
1049 GAtDisconnectFunc disconnect, gpointer user_data)
1054 chat->user_disconnect = disconnect;
1055 chat->user_disconnect_data = user_data;
1060 gboolean g_at_chat_set_debug(GAtChat *chat, GAtDebugFunc func, gpointer user)
1065 chat->debugf = func;
1066 chat->debug_data = user;
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)
1077 struct at_command *c;
1079 if (chat == NULL || chat->command_queue == NULL)
1082 c = at_command_create(cmd, prefix_list, expect_pdu, listing, func,
1083 user_data, notify, FALSE);
1088 c->id = chat->next_cmd_id++;
1090 g_queue_push_tail(chat->command_queue, c);
1092 if (g_queue_get_length(chat->command_queue) == 1)
1093 g_at_chat_wakeup_writer(chat);
1098 guint g_at_chat_send(GAtChat *chat, const char *cmd,
1099 const char **prefix_list, GAtResultFunc func,
1100 gpointer user_data, GDestroyNotify notify)
1102 return send_common(chat, cmd, prefix_list, FALSE, NULL, func,
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)
1111 if (listing == NULL)
1114 return send_common(chat, cmd, prefix_list, FALSE, listing, func,
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)
1123 if (listing == NULL)
1126 return send_common(chat, cmd, prefix_list, TRUE, listing, func,
1130 gboolean g_at_chat_cancel(GAtChat *chat, guint id)
1134 if (chat == NULL || chat->command_queue == NULL)
1137 /* We use id 0 for wakeup commands */
1141 l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
1142 at_command_compare_by_id);
1147 if (l == g_queue_peek_head(chat->command_queue) &&
1148 chat->cmd_bytes_written > 0) {
1149 struct at_command *c = l->data;
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
1157 at_command_destroy(l->data);
1158 g_queue_remove(chat->command_queue, l->data);
1164 gboolean g_at_chat_cancel_all(GAtChat *chat)
1167 struct at_command *c;
1169 if (chat == NULL || chat->command_queue == NULL)
1172 while ((c = g_queue_peek_nth(chat->command_queue, n)) != NULL) {
1178 if (n == 0 && chat->cmd_bytes_written > 0) {
1184 at_command_destroy(c);
1185 g_queue_remove(chat->command_queue, c);
1191 static struct at_notify *at_notify_create(GAtChat *chat, const char *prefix,
1194 struct at_notify *notify;
1197 key = g_strdup(prefix);
1202 notify = g_try_new0(struct at_notify, 1);
1211 g_hash_table_insert(chat->notify_list, key, notify);
1216 guint g_at_chat_register(GAtChat *chat, const char *prefix,
1217 GAtNotifyFunc func, gboolean expect_pdu,
1219 GDestroyNotify destroy_notify)
1221 struct at_notify *notify;
1222 struct at_notify_node *node;
1224 if (chat == NULL || chat->notify_list == NULL)
1230 if (prefix == NULL || strlen(prefix) == 0)
1233 notify = g_hash_table_lookup(chat->notify_list, prefix);
1236 notify = at_notify_create(chat, prefix, expect_pdu);
1238 if (!notify || notify->pdu != expect_pdu)
1241 node = g_try_new0(struct at_notify_node, 1);
1246 node->id = chat->next_notify_id++;
1247 node->callback = func;
1248 node->user_data = user_data;
1249 node->notify = destroy_notify;
1251 notify->nodes = g_slist_prepend(notify->nodes, node);
1256 gboolean g_at_chat_unregister(GAtChat *chat, guint id)
1258 GHashTableIter iter;
1259 struct at_notify *notify;
1261 gpointer key, value;
1264 if (chat == NULL || chat->notify_list == NULL)
1267 g_hash_table_iter_init(&iter, chat->notify_list);
1269 while (g_hash_table_iter_next(&iter, &key, &value)) {
1273 l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
1274 at_notify_node_compare_by_id);
1279 at_notify_node_destroy(l->data);
1280 notify->nodes = g_slist_remove(notify->nodes, l->data);
1282 if (notify->nodes == NULL)
1283 g_hash_table_iter_remove(&iter);
1291 gboolean g_at_chat_unregister_all(GAtChat *chat)
1293 GHashTableIter iter;
1294 struct at_notify *notify;
1296 gpointer key, value;
1299 if (chat == NULL || chat->notify_list == NULL)
1302 g_hash_table_iter_init(&iter, chat->notify_list);
1304 while (g_hash_table_iter_next(&iter, &key, &value)) {
1308 for (l = notify->nodes; l; l = l->next)
1309 at_notify_node_destroy(l->data);
1311 g_slist_free(notify->nodes);
1312 notify->nodes = NULL;
1313 g_hash_table_iter_remove(&iter);
1319 gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
1320 unsigned int timeout, unsigned int msec)
1326 g_free(chat->wakeup);
1328 chat->wakeup = g_strdup(cmd);
1329 chat->inactivity_time = (gdouble)msec / 1000;
1330 chat->wakeup_timeout = timeout;