3 * AT chat library with GLib integration
5 * Copyright (C) 2008-2009 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"
36 #include "gatresult.h"
39 /* #define WRITE_SCHEDULER_DEBUG 1 */
41 static void g_at_chat_wakeup_writer(GAtChat *chat);
44 PARSER_STATE_IDLE = 0,
45 PARSER_STATE_INITIAL_CR,
46 PARSER_STATE_INITIAL_LF,
47 PARSER_STATE_RESPONSE,
48 PARSER_STATE_TERMINATOR_CR,
49 PARSER_STATE_RESPONSE_COMPLETE,
50 PARSER_STATE_GUESS_MULTILINE_RESPONSE,
51 PARSER_STATE_MULTILINE_RESPONSE,
52 PARSER_STATE_MULTILINE_TERMINATOR_CR,
53 PARSER_STATE_MULTILINE_COMPLETE,
56 PARSER_STATE_PDU_COMPLETE,
58 PARSER_STATE_PROMPT_COMPLETE
65 GAtResultFunc callback;
66 GAtNotifyFunc listing;
68 GDestroyNotify notify;
71 struct at_notify_node {
73 GAtNotifyFunc callback;
75 GDestroyNotify notify;
84 gint ref_count; /* Ref count */
85 guint next_cmd_id; /* Next command id */
86 guint next_notify_id; /* Next notify id */
87 guint read_watch; /* GSource read id, 0 if none */
88 guint write_watch; /* GSource write id, 0 if none */
89 GIOChannel *channel; /* channel */
90 GQueue *command_queue; /* Command queue */
91 guint cmd_bytes_written; /* bytes written from cmd */
92 GHashTable *notify_list; /* List of notification reg */
93 GAtDisconnectFunc user_disconnect; /* user disconnect func */
94 gpointer user_disconnect_data; /* user disconnect data */
95 struct ring_buffer *buf; /* Current read buffer */
96 guint read_so_far; /* Number of bytes processed */
97 gboolean disconnecting; /* Whether we're disconnecting */
98 enum chat_state state; /* Current chat state */
100 char *pdu_notify; /* Unsolicited Resp w/ PDU */
101 GSList *response_lines; /* char * lines of the response */
102 char *wakeup; /* command sent to wakeup modem */
103 gdouble inactivity_time; /* Period of inactivity */
104 guint wakeup_timeout; /* How long to wait for resp */
105 GTimer *wakeup_timer; /* Keep track of elapsed time */
108 static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
110 const struct at_notify_node *node = a;
111 guint id = GPOINTER_TO_UINT(b);
122 static void at_notify_node_destroy(struct at_notify_node *node)
125 node->notify(node->user_data);
130 static void at_notify_destroy(struct at_notify *notify)
132 g_slist_foreach(notify->nodes, (GFunc) at_notify_node_destroy, NULL);
136 static gint at_command_compare_by_id(gconstpointer a, gconstpointer b)
138 const struct at_command *command = a;
139 guint id = GPOINTER_TO_UINT(b);
141 if (command->id < id)
144 if (command->id > id)
150 static struct at_command *at_command_create(const char *cmd,
151 const char **prefix_list,
152 GAtNotifyFunc listing,
155 GDestroyNotify notify)
157 struct at_command *c;
159 char **prefixes = NULL;
162 int num_prefixes = 0;
165 while (prefix_list[num_prefixes])
168 prefixes = g_new(char *, num_prefixes + 1);
170 for (i = 0; i < num_prefixes; i++)
171 prefixes[i] = strdup(prefix_list[i]);
173 prefixes[num_prefixes] = NULL;
176 c = g_try_new0(struct at_command, 1);
182 c->cmd = g_try_new(char, len + 2);
189 memcpy(c->cmd, cmd, len);
191 /* If we have embedded '\r' then this is a command expecting a prompt
192 * from the modem. Embed Ctrl-Z at the very end automatically
194 if (strchr(cmd, '\r'))
199 c->cmd[len+1] = '\0';
201 c->prefixes = prefixes;
203 c->listing = listing;
204 c->user_data = user_data;
210 static void at_command_destroy(struct at_command *cmd)
213 cmd->notify(cmd->user_data);
215 g_strfreev(cmd->prefixes);
220 static void g_at_chat_cleanup(GAtChat *chat)
222 struct at_command *c;
224 ring_buffer_free(chat->buf);
227 /* Cleanup pending commands */
228 while ((c = g_queue_pop_head(chat->command_queue)))
229 at_command_destroy(c);
231 g_queue_free(chat->command_queue);
232 chat->command_queue = NULL;
234 /* Cleanup any response lines we have pending */
235 g_slist_foreach(chat->response_lines, (GFunc)g_free, NULL);
236 g_slist_free(chat->response_lines);
237 chat->response_lines = NULL;
239 /* Cleanup registered notifications */
240 g_hash_table_destroy(chat->notify_list);
241 chat->notify_list = NULL;
243 if (chat->pdu_notify) {
244 g_free(chat->pdu_notify);
245 chat->pdu_notify = NULL;
249 g_free(chat->wakeup);
253 if (chat->wakeup_timer) {
254 g_timer_destroy(chat->wakeup_timer);
255 chat->wakeup_timer = 0;
259 static void read_watcher_destroy_notify(GAtChat *chat)
261 chat->read_watch = 0;
263 if (chat->disconnecting)
266 chat->channel = NULL;
268 g_at_chat_cleanup(chat);
270 if (chat->user_disconnect)
271 chat->user_disconnect(chat->user_disconnect_data);
274 static void write_watcher_destroy_notify(GAtChat *chat)
276 chat->write_watch = 0;
279 static void at_notify_call_callback(gpointer data, gpointer user_data)
281 struct at_notify_node *node = data;
282 GAtResult *result = user_data;
284 node->callback(result, node->user_data);
287 static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
290 struct at_notify *notify;
293 gboolean ret = FALSE;
296 g_hash_table_iter_init(&iter, chat->notify_list);
298 result.final_or_pdu = 0;
300 while (g_hash_table_iter_next(&iter, &key, &value)) {
304 if (!g_str_has_prefix(line, key))
308 chat->pdu_notify = line;
309 chat->state = PARSER_STATE_PDU;
314 result.lines = g_slist_prepend(NULL, line);
316 g_slist_foreach(notify->nodes, at_notify_call_callback,
322 g_slist_free(result.lines);
324 chat->state = PARSER_STATE_IDLE;
330 static void g_at_chat_finish_command(GAtChat *p, gboolean ok,
333 struct at_command *cmd = g_queue_pop_head(p->command_queue);
335 /* Cannot happen, but lets be paranoid */
342 p->response_lines = g_slist_reverse(p->response_lines);
344 result.final_or_pdu = final;
345 result.lines = p->response_lines;
347 cmd->callback(ok, &result, cmd->user_data);
350 g_slist_foreach(p->response_lines, (GFunc)g_free, NULL);
351 g_slist_free(p->response_lines);
352 p->response_lines = NULL;
356 at_command_destroy(cmd);
358 p->cmd_bytes_written = 0;
360 if (g_queue_peek_head(p->command_queue))
361 g_at_chat_wakeup_writer(p);
364 struct terminator_info {
365 const char *terminator;
370 static struct terminator_info terminator_table[] = {
372 { "ERROR", -1, FALSE },
373 { "NO DIALTONE", -1, FALSE },
374 { "BUSY", -1, FALSE },
375 { "NO CARRIER", -1, FALSE },
376 { "CONNECT", -1, TRUE },
377 { "NO ANSWER", -1, FALSE },
378 { "+CMS ERROR:", 11, FALSE },
379 { "+CME ERROR:", 11, FALSE },
380 { "+EXT ERROR:", 11, FALSE }
383 static gboolean g_at_chat_handle_command_response(GAtChat *p,
384 struct at_command *cmd,
388 int size = sizeof(terminator_table) / sizeof(struct terminator_info);
390 p->state = PARSER_STATE_IDLE;
392 for (i = 0; i < size; i++) {
393 struct terminator_info *info = &terminator_table[i];
395 if (info->len == -1 && !strcmp(line, info->terminator)) {
396 g_at_chat_finish_command(p, info->success, line);
401 !strncmp(line, info->terminator, info->len)) {
402 g_at_chat_finish_command(p, info->success, line);
410 for (i = 0; cmd->prefixes[i]; i++)
411 if (g_str_has_prefix(line, cmd->prefixes[i]))
418 if (!(p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
419 p->state = PARSER_STATE_GUESS_MULTILINE_RESPONSE;
424 result.lines = g_slist_prepend(NULL, line);
425 result.final_or_pdu = NULL;
427 cmd->listing(&result, cmd->user_data);
429 g_slist_free(result.lines);
432 p->response_lines = g_slist_prepend(p->response_lines, line);
437 static void have_line(GAtChat *p, gboolean strip_preceding)
439 /* We're not going to copy terminal <CR><LF> */
440 unsigned int len = p->read_so_far - 2;
442 struct at_command *cmd;
444 /* If we have preceding <CR><LF> modify the len */
448 /* Make sure we have terminal null */
449 str = g_try_new(char, len + 1);
452 ring_buffer_drain(p->buf, p->read_so_far);
457 ring_buffer_drain(p->buf, 2);
458 ring_buffer_read(p->buf, str, len);
459 ring_buffer_drain(p->buf, 2);
463 /* Check for echo, this should not happen, but lets be paranoid */
464 if (!strncmp(str, "AT", 2) == TRUE)
467 cmd = g_queue_peek_head(p->command_queue);
470 char c = cmd->cmd[p->cmd_bytes_written - 1];
472 /* We check that we have submitted a terminator, in which case
473 * a command might have failed or completed successfully
475 * In the generic case, \r is at the end of the command, so we
476 * know the entire command has been submitted. In the case of
477 * commands like CMGS, every \r or Ctrl-Z might result in a
478 * final response from the modem, so we check this as well.
480 if ((c == '\r' || c == 26) &&
481 g_at_chat_handle_command_response(p, cmd, str))
485 if (g_at_chat_match_notify(p, str) == TRUE)
489 /* No matches & no commands active, ignore line */
491 p->state = PARSER_STATE_IDLE;
494 static void have_pdu(GAtChat *p)
496 unsigned int len = p->read_so_far - 2;
499 struct at_notify *notify;
504 pdu = g_try_new(char, len + 1);
507 ring_buffer_drain(p->buf, p->read_so_far);
511 ring_buffer_read(p->buf, pdu, len);
512 ring_buffer_drain(p->buf, 2);
516 result.lines = g_slist_prepend(NULL, p->pdu_notify);
517 result.final_or_pdu = pdu;
519 g_hash_table_iter_init(&iter, p->notify_list);
521 while (g_hash_table_iter_next(&iter, &key, &value)) {
525 if (!g_str_has_prefix(p->pdu_notify, prefix))
531 g_slist_foreach(notify->nodes, at_notify_call_callback,
535 g_slist_free(result.lines);
538 g_free(p->pdu_notify);
539 p->pdu_notify = NULL;
544 p->state = PARSER_STATE_IDLE;
547 static inline void parse_char(GAtChat *chat, char byte)
549 switch (chat->state) {
550 case PARSER_STATE_IDLE:
552 chat->state = PARSER_STATE_INITIAL_CR;
553 else if (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) {
555 chat->state = PARSER_STATE_PROMPT;
557 chat->state = PARSER_STATE_RESPONSE;
561 case PARSER_STATE_INITIAL_CR:
563 chat->state = PARSER_STATE_INITIAL_LF;
564 else if (byte != '\r' && /* Echo & no <CR><LF>?! */
565 (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
566 chat->state = PARSER_STATE_RESPONSE;
567 else if (byte != '\r')
568 chat->state = PARSER_STATE_IDLE;
571 case PARSER_STATE_INITIAL_LF:
573 chat->state = PARSER_STATE_TERMINATOR_CR;
574 else if (byte == '>')
575 chat->state = PARSER_STATE_PROMPT;
577 chat->state = PARSER_STATE_RESPONSE;
580 case PARSER_STATE_RESPONSE:
582 chat->state = PARSER_STATE_TERMINATOR_CR;
585 case PARSER_STATE_TERMINATOR_CR:
587 chat->state = PARSER_STATE_RESPONSE_COMPLETE;
589 chat->state = PARSER_STATE_IDLE;
592 case PARSER_STATE_GUESS_MULTILINE_RESPONSE:
594 chat->state = PARSER_STATE_INITIAL_CR;
596 chat->state = PARSER_STATE_MULTILINE_RESPONSE;
599 case PARSER_STATE_MULTILINE_RESPONSE:
601 chat->state = PARSER_STATE_MULTILINE_TERMINATOR_CR;
604 case PARSER_STATE_MULTILINE_TERMINATOR_CR:
606 chat->state = PARSER_STATE_MULTILINE_COMPLETE;
609 case PARSER_STATE_PDU:
611 chat->state = PARSER_STATE_PDU_CR;
614 case PARSER_STATE_PDU_CR:
616 chat->state = PARSER_STATE_PDU_COMPLETE;
619 case PARSER_STATE_PROMPT:
621 chat->state = PARSER_STATE_PROMPT_COMPLETE;
623 chat->state = PARSER_STATE_RESPONSE;
626 case PARSER_STATE_RESPONSE_COMPLETE:
627 case PARSER_STATE_PDU_COMPLETE:
628 case PARSER_STATE_MULTILINE_COMPLETE:
630 /* This really shouldn't happen */
636 static void new_bytes(GAtChat *p)
638 unsigned int len = ring_buffer_len(p->buf);
639 unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
640 unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
642 while (p->read_so_far < len) {
648 if (p->read_so_far == wrap) {
649 buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
653 if (p->state == PARSER_STATE_RESPONSE_COMPLETE) {
654 gboolean strip_preceding;
656 if (p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF)
657 strip_preceding = FALSE;
659 strip_preceding = TRUE;
661 len -= p->read_so_far;
662 wrap -= p->read_so_far;
664 have_line(p, strip_preceding);
667 } else if (p->state == PARSER_STATE_MULTILINE_COMPLETE) {
668 len -= p->read_so_far;
669 wrap -= p->read_so_far;
674 } else if (p->state == PARSER_STATE_PDU_COMPLETE) {
675 len -= p->read_so_far;
676 wrap -= p->read_so_far;
678 /* Some modems like the TI Calypso send a CMT style
679 * notification with an extra CRLF thrown in
681 if ((p->flags & G_AT_CHAT_FLAG_EXTRA_PDU_CRLF) &&
682 p->read_so_far == 2) {
683 p->state = PARSER_STATE_PDU;
684 ring_buffer_drain(p->buf, p->read_so_far);
689 } else if (p->state == PARSER_STATE_INITIAL_CR) {
690 len -= p->read_so_far - 1;
691 wrap -= p->read_so_far - 1;
693 ring_buffer_drain(p->buf, p->read_so_far - 1);
696 } else if (p->state == PARSER_STATE_PROMPT_COMPLETE) {
697 len -= p->read_so_far;
698 wrap -= p->read_so_far;
700 g_at_chat_wakeup_writer(p);
702 ring_buffer_drain(p->buf, p->read_so_far);
706 p->state = PARSER_STATE_IDLE;
710 if (p->state == PARSER_STATE_IDLE && p->read_so_far > 0) {
711 ring_buffer_drain(p->buf, p->read_so_far);
716 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
720 GAtChat *chat = data;
724 gsize total_read = 0;
726 if (cond & G_IO_NVAL)
729 /* Regardless of condition, try to read all the data available */
733 toread = ring_buffer_avail_no_wrap(chat->buf);
735 /* We're going to start overflowing the buffer
736 * this cannot happen under normal circumstances, so probably
737 * the channel is getting garbage, drop off
740 if (chat->state == PARSER_STATE_RESPONSE)
743 err = G_IO_ERROR_AGAIN;
747 buf = ring_buffer_write_ptr(chat->buf);
749 err = g_io_channel_read(channel, (char *) buf, toread, &rbytes);
751 total_read += rbytes;
754 ring_buffer_write_advance(chat->buf, rbytes);
756 } while (err == G_IO_ERROR_NONE && rbytes > 0);
761 if (cond & (G_IO_HUP | G_IO_ERR))
764 if (err == G_IO_ERROR_NONE && rbytes == 0)
767 if (err != G_IO_ERROR_NONE && err != G_IO_ERROR_AGAIN)
773 static gboolean wakeup_no_response(gpointer user)
775 GAtChat *chat = user;
776 struct at_command *cmd = g_queue_peek_head(chat->command_queue);
778 /* Sometimes during startup the modem is still in the ready state
779 * and might acknowledge our 'wakeup' command. In that case don't
780 * timeout the wrong command
782 if (cmd == NULL || cmd->id != 0)
785 g_at_chat_finish_command(chat, FALSE, NULL);
790 static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
793 GAtChat *chat = data;
794 struct at_command *cmd;
800 gboolean wakeup_first = FALSE;
801 #ifdef WRITE_SCHEDULER_DEBUG
805 if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
808 /* Grab the first command off the queue and write as
809 * much of it as we can
811 cmd = g_queue_peek_head(chat->command_queue);
813 /* For some reason command queue is empty, cancel write watcher */
817 len = strlen(cmd->cmd);
819 /* For some reason write watcher fired, but we've already
820 * written the entire command out to the io channel,
821 * cancel write watcher
823 if (chat->cmd_bytes_written >= len)
827 if (!chat->wakeup_timer) {
829 chat->wakeup_timer = g_timer_new();
831 } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
832 chat->inactivity_time)
836 if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) {
837 cmd = at_command_create(chat->wakeup, NULL, NULL, NULL,
843 g_queue_push_head(chat->command_queue, cmd);
845 len = strlen(chat->wakeup);
847 g_timeout_add(chat->wakeup_timeout, wakeup_no_response,
851 towrite = len - chat->cmd_bytes_written;
853 cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r');
856 towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1;
858 #ifdef WRITE_SCHEDULER_DEBUG
865 err = g_io_channel_write(chat->channel,
866 cmd->cmd + chat->cmd_bytes_written,
867 #ifdef WRITE_SCHEDULER_DEBUG
874 if (err != G_IO_ERROR_NONE) {
875 g_at_chat_shutdown(chat);
879 chat->cmd_bytes_written += bytes_written;
881 if (bytes_written < towrite)
884 /* Full command submitted, update timer */
885 if (chat->wakeup_timer)
886 g_timer_start(chat->wakeup_timer);
891 static void g_at_chat_wakeup_writer(GAtChat *chat)
893 if (chat->write_watch != 0)
896 chat->write_watch = g_io_add_watch_full(chat->channel,
898 G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
899 can_write_data, chat,
900 (GDestroyNotify)write_watcher_destroy_notify);
903 GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
911 chat = g_try_new0(GAtChat, 1);
916 chat->next_cmd_id = 1;
917 chat->next_notify_id = 1;
920 chat->buf = ring_buffer_new(4096);
925 chat->command_queue = g_queue_new();
927 if (!chat->command_queue)
930 chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal,
931 g_free, (GDestroyNotify)at_notify_destroy);
933 if (g_io_channel_set_encoding(channel, NULL, NULL) !=
937 io_flags = g_io_channel_get_flags(channel);
939 io_flags |= G_IO_FLAG_NONBLOCK;
941 if (g_io_channel_set_flags(channel, io_flags, NULL) !=
945 g_io_channel_set_close_on_unref(channel, TRUE);
947 chat->channel = channel;
948 chat->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
949 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
951 (GDestroyNotify)read_watcher_destroy_notify);
957 ring_buffer_free(chat->buf);
959 if (chat->command_queue)
960 g_queue_free(chat->command_queue);
962 if (chat->notify_list)
963 g_hash_table_destroy(chat->notify_list);
969 int g_at_chat_get_flags(GAtChat *chat)
977 void g_at_chat_set_flags(GAtChat *chat, int flags)
985 static int open_device(const char *device)
990 fd = open(device, O_RDWR | O_NOCTTY);
994 tcflush(fd, TCIOFLUSH);
996 /* Switch TTY to raw mode */
997 memset(&ti, 0, sizeof(ti));
1000 tcsetattr(fd, TCSANOW, &ti);
1005 GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
1007 GIOChannel *channel;
1010 fd = open_device(device);
1014 channel = g_io_channel_unix_new(fd);
1020 return g_at_chat_new(channel, flags);
1023 GAtChat *g_at_chat_ref(GAtChat *chat)
1028 g_atomic_int_inc(&chat->ref_count);
1033 void g_at_chat_unref(GAtChat *chat)
1040 is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
1043 g_at_chat_shutdown(chat);
1045 g_at_chat_cleanup(chat);
1050 gboolean g_at_chat_shutdown(GAtChat *chat)
1052 if (chat->channel == NULL)
1055 chat->disconnecting = TRUE;
1057 if (chat->read_watch)
1058 g_source_remove(chat->read_watch);
1060 if (chat->write_watch)
1061 g_source_remove(chat->write_watch);
1066 gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
1067 GAtDisconnectFunc disconnect, gpointer user_data)
1072 chat->user_disconnect = disconnect;
1073 chat->user_disconnect_data = user_data;
1078 static guint send_common(GAtChat *chat, const char *cmd,
1079 const char **prefix_list,
1080 GAtNotifyFunc listing, GAtResultFunc func,
1081 gpointer user_data, GDestroyNotify notify)
1083 struct at_command *c;
1085 if (chat == NULL || chat->command_queue == NULL)
1088 c = at_command_create(cmd, prefix_list, listing, func,
1094 c->id = chat->next_cmd_id++;
1096 g_queue_push_tail(chat->command_queue, c);
1098 if (g_queue_get_length(chat->command_queue) == 1)
1099 g_at_chat_wakeup_writer(chat);
1104 guint g_at_chat_send(GAtChat *chat, const char *cmd,
1105 const char **prefix_list, GAtResultFunc func,
1106 gpointer user_data, GDestroyNotify notify)
1108 return send_common(chat, cmd, prefix_list, NULL, func,
1112 guint g_at_chat_send_listing(GAtChat *chat, const char *cmd,
1113 const char **prefix_list,
1114 GAtNotifyFunc listing, GAtResultFunc func,
1115 gpointer user_data, GDestroyNotify notify)
1117 if (listing == NULL)
1120 return send_common(chat, cmd, prefix_list, listing, func,
1124 gboolean g_at_chat_cancel(GAtChat *chat, guint id)
1128 if (chat == NULL || chat->command_queue == NULL)
1131 l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
1132 at_command_compare_by_id);
1137 if (l == g_queue_peek_head(chat->command_queue)) {
1138 struct at_command *c = l->data;
1140 /* We can't actually remove it since it is most likely
1141 * already in progress, just null out the callback
1142 * so it won't be called
1146 at_command_destroy(l->data);
1147 g_queue_remove(chat->command_queue, l->data);
1153 static struct at_notify *at_notify_create(GAtChat *chat, const char *prefix,
1156 struct at_notify *notify;
1159 key = g_strdup(prefix);
1164 notify = g_try_new0(struct at_notify, 1);
1173 g_hash_table_insert(chat->notify_list, key, notify);
1178 guint g_at_chat_register(GAtChat *chat, const char *prefix,
1179 GAtNotifyFunc func, gboolean expect_pdu,
1181 GDestroyNotify destroy_notify)
1183 struct at_notify *notify;
1184 struct at_notify_node *node;
1186 if (chat == NULL || chat->notify_list == NULL)
1192 if (prefix == NULL || strlen(prefix) == 0)
1195 notify = g_hash_table_lookup(chat->notify_list, prefix);
1198 notify = at_notify_create(chat, prefix, expect_pdu);
1200 if (!notify || notify->pdu != expect_pdu)
1203 node = g_try_new0(struct at_notify_node, 1);
1208 node->id = chat->next_notify_id++;
1209 node->callback = func;
1210 node->user_data = user_data;
1211 node->notify = destroy_notify;
1213 notify->nodes = g_slist_prepend(notify->nodes, node);
1218 gboolean g_at_chat_unregister(GAtChat *chat, guint id)
1220 GHashTableIter iter;
1221 struct at_notify *notify;
1223 gpointer key, value;
1226 if (chat == NULL || chat->notify_list == NULL)
1229 g_hash_table_iter_init(&iter, chat->notify_list);
1231 while (g_hash_table_iter_next(&iter, &key, &value)) {
1235 l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
1236 at_notify_node_compare_by_id);
1241 at_notify_node_destroy(l->data);
1242 notify->nodes = g_slist_remove(notify->nodes, l->data);
1244 if (notify->nodes == NULL)
1245 g_hash_table_iter_remove(&iter);
1253 gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
1254 unsigned int timeout, unsigned int msec)
1260 g_free(chat->wakeup);
1262 chat->wakeup = g_strdup(cmd);
1263 chat->inactivity_time = (gdouble)msec / 1000;
1264 chat->wakeup_timeout = timeout;