3 * AT chat library with GLib integration
5 * Copyright (C) 2008-2011 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"
39 /* #define WRITE_SCHEDULER_DEBUG 1 */
41 #define COMMAND_FLAG_EXPECT_PDU 0x1
42 #define COMMAND_FLAG_EXPECT_SHORT_PROMPT 0x2
45 static void chat_wakeup_writer(struct at_chat *chat);
47 static const char *none_prefix[] = { NULL };
55 GAtResultFunc callback;
56 GAtNotifyFunc listing;
58 GDestroyNotify notify;
61 struct at_notify_node {
64 GAtNotifyFunc callback;
66 GDestroyNotify notify;
70 typedef gboolean (*node_remove_func)(struct at_notify_node *node,
79 gint ref_count; /* Ref count */
80 guint next_cmd_id; /* Next command id */
81 guint next_notify_id; /* Next notify id */
82 guint next_gid; /* Next group id */
83 GAtIO *io; /* AT IO */
84 GQueue *command_queue; /* Command queue */
85 guint cmd_bytes_written; /* bytes written from cmd */
86 GHashTable *notify_list; /* List of notification reg */
87 GAtDisconnectFunc user_disconnect; /* user disconnect func */
88 gpointer user_disconnect_data; /* user disconnect data */
89 guint read_so_far; /* Number of bytes processed */
90 gboolean suspended; /* Are we suspended? */
91 GAtDebugFunc debugf; /* debugging output function */
92 gpointer debug_data; /* Data to pass to debug func */
93 char *pdu_notify; /* Unsolicited Resp w/ PDU */
94 GSList *response_lines; /* char * lines of the response */
95 char *wakeup; /* command sent to wakeup modem */
97 gdouble inactivity_time; /* Period of inactivity */
98 guint wakeup_timeout; /* How long to wait for resp */
99 GTimer *wakeup_timer; /* Keep track of elapsed time */
101 gboolean destroyed; /* Re-entrancy guard */
102 gboolean in_read_handler; /* Re-entrancy guard */
104 GSList *terminator_list; /* Non-standard terminator */
105 guint16 terminator_blacklist; /* Blacklisted terinators */
110 struct at_chat *parent;
115 struct terminator_info {
121 static gboolean node_is_destroyed(struct at_notify_node *node, gpointer user)
123 return node->destroyed;
126 static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
128 const struct at_notify_node *node = a;
129 guint id = GPOINTER_TO_UINT(b);
140 static void at_notify_node_destroy(gpointer data, gpointer user_data)
142 struct at_notify_node *node = data;
145 node->notify(node->user_data);
150 static void at_notify_destroy(gpointer user_data)
152 struct at_notify *notify = user_data;
154 g_slist_foreach(notify->nodes, at_notify_node_destroy, NULL);
155 g_slist_free(notify->nodes);
159 static gint at_command_compare_by_id(gconstpointer a, gconstpointer b)
161 const struct at_command *command = a;
162 guint id = GPOINTER_TO_UINT(b);
164 if (command->id < id)
167 if (command->id > id)
173 static gboolean at_chat_unregister_all(struct at_chat *chat,
175 node_remove_func func,
179 struct at_notify *notify;
180 struct at_notify_node *node;
186 if (chat->notify_list == NULL)
189 g_hash_table_iter_init(&iter, chat->notify_list);
191 while (g_hash_table_iter_next(&iter, &key, &value)) {
200 if (func(node, userdata) != TRUE) {
207 node->destroyed = TRUE;
216 notify->nodes = c->next;
218 at_notify_node_destroy(node, NULL);
225 if (notify->nodes == NULL)
226 g_hash_table_iter_remove(&iter);
232 static struct at_command *at_command_create(guint gid, const char *cmd,
233 const char **prefix_list,
235 GAtNotifyFunc listing,
238 GDestroyNotify notify,
241 struct at_command *c;
243 char **prefixes = NULL;
246 int num_prefixes = 0;
249 while (prefix_list[num_prefixes])
252 prefixes = g_new(char *, num_prefixes + 1);
254 for (i = 0; i < num_prefixes; i++)
255 prefixes[i] = strdup(prefix_list[i]);
257 prefixes[num_prefixes] = NULL;
260 c = g_try_new0(struct at_command, 1);
265 c->cmd = g_try_new(char, len + 2);
266 if (c->cmd == NULL) {
271 memcpy(c->cmd, cmd, len);
273 /* If we have embedded '\r' then this is a command expecting a prompt
274 * from the modem. Embed Ctrl-Z at the very end automatically
276 if (wakeup == FALSE) {
277 if (strchr(cmd, '\r'))
289 c->prefixes = prefixes;
291 c->listing = listing;
292 c->user_data = user_data;
298 static void at_command_destroy(struct at_command *cmd)
301 cmd->notify(cmd->user_data);
303 g_strfreev(cmd->prefixes);
308 static void free_terminator(struct terminator_info *info)
310 g_free(info->terminator);
311 info->terminator = NULL;
316 static void chat_cleanup(struct at_chat *chat)
318 struct at_command *c;
320 /* Cleanup pending commands */
321 while ((c = g_queue_pop_head(chat->command_queue)))
322 at_command_destroy(c);
324 g_queue_free(chat->command_queue);
325 chat->command_queue = NULL;
327 /* Cleanup any response lines we have pending */
328 g_slist_foreach(chat->response_lines, (GFunc)g_free, NULL);
329 g_slist_free(chat->response_lines);
330 chat->response_lines = NULL;
332 /* Cleanup registered notifications */
333 g_hash_table_destroy(chat->notify_list);
334 chat->notify_list = NULL;
336 if (chat->pdu_notify) {
337 g_free(chat->pdu_notify);
338 chat->pdu_notify = NULL;
342 g_free(chat->wakeup);
346 if (chat->wakeup_timer) {
347 g_timer_destroy(chat->wakeup_timer);
348 chat->wakeup_timer = 0;
351 if (chat->timeout_source) {
352 g_source_remove(chat->timeout_source);
353 chat->timeout_source = 0;
356 g_at_syntax_unref(chat->syntax);
359 if (chat->terminator_list) {
360 g_slist_foreach(chat->terminator_list,
361 (GFunc)free_terminator, NULL);
362 g_slist_free(chat->terminator_list);
363 chat->terminator_list = NULL;
367 static void io_disconnect(gpointer user_data)
369 struct at_chat *chat = user_data;
372 g_at_io_unref(chat->io);
375 if (chat->user_disconnect)
376 chat->user_disconnect(chat->user_disconnect_data);
379 static void at_notify_call_callback(gpointer data, gpointer user_data)
381 struct at_notify_node *node = data;
382 GAtResult *result = user_data;
384 node->callback(result, node->user_data);
387 static gboolean at_chat_match_notify(struct at_chat *chat, char *line)
390 struct at_notify *notify;
392 gboolean ret = FALSE;
395 g_hash_table_iter_init(&iter, chat->notify_list);
397 result.final_or_pdu = 0;
399 chat->in_notify = TRUE;
401 while (g_hash_table_iter_next(&iter, &key, &value)) {
404 if (!g_str_has_prefix(line, key))
408 chat->pdu_notify = line;
410 if (chat->syntax->set_hint)
411 chat->syntax->set_hint(chat->syntax,
412 G_AT_SYNTAX_EXPECT_PDU);
416 if (result.lines == NULL)
417 result.lines = g_slist_prepend(NULL, line);
419 g_slist_foreach(notify->nodes, at_notify_call_callback,
424 chat->in_notify = FALSE;
427 g_slist_free(result.lines);
430 at_chat_unregister_all(chat, FALSE, node_is_destroyed, NULL);
436 static void at_chat_finish_command(struct at_chat *p, gboolean ok, char *final)
438 struct at_command *cmd = g_queue_pop_head(p->command_queue);
439 GSList *response_lines;
441 /* Cannot happen, but lets be paranoid */
445 p->cmd_bytes_written = 0;
447 if (g_queue_peek_head(p->command_queue))
448 chat_wakeup_writer(p);
450 response_lines = p->response_lines;
451 p->response_lines = NULL;
456 response_lines = g_slist_reverse(response_lines);
458 result.final_or_pdu = final;
459 result.lines = response_lines;
461 cmd->callback(ok, &result, cmd->user_data);
464 g_slist_foreach(response_lines, (GFunc)g_free, NULL);
465 g_slist_free(response_lines);
468 at_command_destroy(cmd);
471 static struct terminator_info terminator_table[] = {
473 { "ERROR", -1, FALSE },
474 { "NO DIALTONE", -1, FALSE },
475 { "BUSY", -1, FALSE },
476 { "NO CARRIER", -1, FALSE },
477 { "CONNECT", 7, TRUE },
478 { "NO ANSWER", -1, FALSE },
479 { "+CMS ERROR:", 11, FALSE },
480 { "+CME ERROR:", 11, FALSE },
481 { "+EXT ERROR:", 11, FALSE }
484 static void at_chat_add_terminator(struct at_chat *chat, char *terminator,
485 int len, gboolean success)
487 struct terminator_info *info = g_new0(struct terminator_info, 1);
488 info->terminator = g_strdup(terminator);
490 info->success = success;
491 chat->terminator_list = g_slist_prepend(chat->terminator_list, info);
494 static void at_chat_blacklist_terminator(struct at_chat *chat,
495 GAtChatTerminator terminator)
497 chat->terminator_blacklist |= 1 << terminator;
500 static gboolean check_terminator(struct terminator_info *info, char *line)
502 if (info->len == -1 && !strcmp(line, info->terminator))
505 if (info->len > 0 && !strncmp(line, info->terminator, info->len))
511 static gboolean at_chat_handle_command_response(struct at_chat *p,
512 struct at_command *cmd,
516 int size = sizeof(terminator_table) / sizeof(struct terminator_info);
520 for (i = 0; i < size; i++) {
521 struct terminator_info *info = &terminator_table[i];
522 if (check_terminator(info, line) &&
523 (p->terminator_blacklist & 1 << i) == 0) {
524 at_chat_finish_command(p, info->success, line);
529 for (l = p->terminator_list; l; l = l->next) {
530 struct terminator_info *info = l->data;
531 if (check_terminator(info, line)) {
532 at_chat_finish_command(p, info->success, line);
540 for (n = 0; cmd->prefixes[n]; n++)
541 if (g_str_has_prefix(line, cmd->prefixes[n]))
548 if (cmd->listing && (cmd->flags & COMMAND_FLAG_EXPECT_PDU))
549 hint = G_AT_SYNTAX_EXPECT_PDU;
551 hint = G_AT_SYNTAX_EXPECT_MULTILINE;
553 if (p->syntax->set_hint)
554 p->syntax->set_hint(p->syntax, hint);
556 if (cmd->listing && (cmd->flags & COMMAND_FLAG_EXPECT_PDU)) {
557 p->pdu_notify = line;
564 result.lines = g_slist_prepend(NULL, line);
565 result.final_or_pdu = NULL;
567 cmd->listing(&result, cmd->user_data);
569 g_slist_free(result.lines);
572 p->response_lines = g_slist_prepend(p->response_lines, line);
577 static void have_line(struct at_chat *p, char *str)
579 /* We're not going to copy terminal <CR><LF> */
580 struct at_command *cmd;
585 /* Check for echo, this should not happen, but lets be paranoid */
586 if (!strncmp(str, "AT", 2))
589 cmd = g_queue_peek_head(p->command_queue);
591 if (cmd && p->cmd_bytes_written > 0) {
592 char c = cmd->cmd[p->cmd_bytes_written - 1];
594 /* We check that we have submitted a terminator, in which case
595 * a command might have failed or completed successfully
597 * In the generic case, \r is at the end of the command, so we
598 * know the entire command has been submitted. In the case of
599 * commands like CMGS, every \r or Ctrl-Z might result in a
600 * final response from the modem, so we check this as well.
602 if ((c == '\r' || c == 26) &&
603 at_chat_handle_command_response(p, cmd, str))
607 if (at_chat_match_notify(p, str) == TRUE)
611 /* No matches & no commands active, ignore line */
615 static void have_notify_pdu(struct at_chat *p, char *pdu, GAtResult *result)
618 struct at_notify *notify;
621 gboolean called = FALSE;
625 g_hash_table_iter_init(&iter, p->notify_list);
627 while (g_hash_table_iter_next(&iter, &key, &value)) {
631 if (!g_str_has_prefix(p->pdu_notify, prefix))
637 g_slist_foreach(notify->nodes, at_notify_call_callback, result);
641 p->in_notify = FALSE;
644 at_chat_unregister_all(p, FALSE, node_is_destroyed, NULL);
647 static void have_pdu(struct at_chat *p, char *pdu)
649 struct at_command *cmd;
651 gboolean listing_pdu = FALSE;
656 result.lines = g_slist_prepend(NULL, p->pdu_notify);
657 result.final_or_pdu = pdu;
659 cmd = g_queue_peek_head(p->command_queue);
661 if (cmd && (cmd->flags & COMMAND_FLAG_EXPECT_PDU) &&
662 p->cmd_bytes_written > 0) {
663 char c = cmd->cmd[p->cmd_bytes_written - 1];
670 cmd->listing(&result, cmd->user_data);
672 if (p->syntax->set_hint)
673 p->syntax->set_hint(p->syntax,
674 G_AT_SYNTAX_EXPECT_MULTILINE);
676 have_notify_pdu(p, pdu, &result);
678 g_slist_free(result.lines);
681 g_free(p->pdu_notify);
682 p->pdu_notify = NULL;
688 static char *extract_line(struct at_chat *p, struct ring_buffer *rbuf)
690 unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
691 unsigned int pos = 0;
692 unsigned char *buf = ring_buffer_read_ptr(rbuf, pos);
693 gboolean in_string = FALSE;
698 while (pos < p->read_so_far) {
699 if (in_string == FALSE && (*buf == '\r' || *buf == '\n')) {
706 in_string = !in_string;
715 buf = ring_buffer_read_ptr(rbuf, pos);
718 line = g_try_new(char, line_length + 1);
720 ring_buffer_drain(rbuf, p->read_so_far);
724 ring_buffer_drain(rbuf, strip_front);
725 ring_buffer_read(rbuf, line, line_length);
726 ring_buffer_drain(rbuf, p->read_so_far - strip_front - line_length);
728 line[line_length] = '\0';
733 static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
735 struct at_chat *p = user_data;
736 unsigned int len = ring_buffer_len(rbuf);
737 unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
738 unsigned char *buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
740 GAtSyntaxResult result;
742 p->in_read_handler = TRUE;
744 while (p->suspended == FALSE && (p->read_so_far < len)) {
745 gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
746 result = p->syntax->feed(p->syntax, (char *)buf, &rbytes);
749 p->read_so_far += rbytes;
751 if (p->read_so_far == wrap) {
752 buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
756 if (result == G_AT_SYNTAX_RESULT_UNSURE)
760 case G_AT_SYNTAX_RESULT_LINE:
761 case G_AT_SYNTAX_RESULT_MULTILINE:
762 have_line(p, extract_line(p, rbuf));
765 case G_AT_SYNTAX_RESULT_PDU:
766 have_pdu(p, extract_line(p, rbuf));
769 case G_AT_SYNTAX_RESULT_PROMPT:
770 chat_wakeup_writer(p);
771 ring_buffer_drain(rbuf, p->read_so_far);
775 ring_buffer_drain(rbuf, p->read_so_far);
779 len -= p->read_so_far;
780 wrap -= p->read_so_far;
784 p->in_read_handler = FALSE;
790 static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data)
792 struct at_chat *chat = user_data;
798 chat->debugf("Finally woke up the modem\n", chat->debug_data);
800 g_source_remove(chat->timeout_source);
801 chat->timeout_source = 0;
804 static gboolean wakeup_no_response(gpointer user_data)
806 struct at_chat *chat = user_data;
807 struct at_command *cmd = g_queue_peek_head(chat->command_queue);
810 chat->debugf("Wakeup got no response\n", chat->debug_data);
815 at_chat_finish_command(chat, FALSE, NULL);
817 cmd = at_command_create(0, chat->wakeup, none_prefix, 0,
818 NULL, wakeup_cb, chat, NULL, TRUE);
820 chat->timeout_source = 0;
824 g_queue_push_head(chat->command_queue, cmd);
829 static gboolean can_write_data(gpointer data)
831 struct at_chat *chat = data;
832 struct at_command *cmd;
837 gboolean wakeup_first = FALSE;
838 #ifdef WRITE_SCHEDULER_DEBUG
842 /* Grab the first command off the queue and write as
843 * much of it as we can
845 cmd = g_queue_peek_head(chat->command_queue);
847 /* For some reason command queue is empty, cancel write watcher */
851 len = strlen(cmd->cmd);
853 /* For some reason write watcher fired, but we've already
854 * written the entire command out to the io channel,
855 * cancel write watcher
857 if (chat->cmd_bytes_written >= len)
861 if (chat->wakeup_timer == NULL) {
863 chat->wakeup_timer = g_timer_new();
865 } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
866 chat->inactivity_time)
870 if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) {
871 cmd = at_command_create(0, chat->wakeup, none_prefix, 0,
872 NULL, wakeup_cb, chat, NULL, TRUE);
876 g_queue_push_head(chat->command_queue, cmd);
878 len = strlen(chat->wakeup);
880 chat->timeout_source = g_timeout_add(chat->wakeup_timeout,
881 wakeup_no_response, chat);
884 towrite = len - chat->cmd_bytes_written;
886 cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r');
889 towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1;
891 #ifdef WRITE_SCHEDULER_DEBUG
898 bytes_written = g_at_io_write(chat->io,
899 cmd->cmd + chat->cmd_bytes_written,
900 #ifdef WRITE_SCHEDULER_DEBUG
907 if (bytes_written == 0)
910 chat->cmd_bytes_written += bytes_written;
912 if (bytes_written < towrite)
916 * If we're expecting a short prompt, set the hint for all lines
917 * sent to the modem except the last
919 if ((cmd->flags & COMMAND_FLAG_EXPECT_SHORT_PROMPT) &&
920 chat->cmd_bytes_written < len &&
921 chat->syntax->set_hint)
922 chat->syntax->set_hint(chat->syntax,
923 G_AT_SYNTAX_EXPECT_SHORT_PROMPT);
925 /* Full command submitted, update timer */
926 if (chat->wakeup_timer)
927 g_timer_start(chat->wakeup_timer);
932 static void chat_wakeup_writer(struct at_chat *chat)
934 g_at_io_set_write_handler(chat->io, can_write_data, chat);
937 static void at_chat_suspend(struct at_chat *chat)
939 chat->suspended = TRUE;
941 g_at_io_set_write_handler(chat->io, NULL, NULL);
942 g_at_io_set_read_handler(chat->io, NULL, NULL);
943 g_at_io_set_debug(chat->io, NULL, NULL);
946 static void at_chat_resume(struct at_chat *chat)
948 chat->suspended = FALSE;
950 if (g_at_io_get_channel(chat->io) == NULL) {
955 g_at_io_set_disconnect_function(chat->io, io_disconnect, chat);
957 g_at_io_set_debug(chat->io, chat->debugf, chat->debug_data);
958 g_at_io_set_read_handler(chat->io, new_bytes, chat);
960 if (g_queue_get_length(chat->command_queue) > 0)
961 chat_wakeup_writer(chat);
964 static void at_chat_unref(struct at_chat *chat)
968 is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
970 if (is_zero == FALSE)
974 at_chat_suspend(chat);
975 g_at_io_unref(chat->io);
980 if (chat->in_read_handler)
981 chat->destroyed = TRUE;
986 static gboolean at_chat_set_disconnect_function(struct at_chat *chat,
987 GAtDisconnectFunc disconnect,
990 chat->user_disconnect = disconnect;
991 chat->user_disconnect_data = user_data;
996 static gboolean at_chat_set_debug(struct at_chat *chat,
997 GAtDebugFunc func, gpointer user_data)
1000 chat->debugf = func;
1001 chat->debug_data = user_data;
1004 g_at_io_set_debug(chat->io, func, user_data);
1009 static gboolean at_chat_set_wakeup_command(struct at_chat *chat,
1011 unsigned int timeout,
1015 g_free(chat->wakeup);
1017 chat->wakeup = g_strdup(cmd);
1018 chat->inactivity_time = (gdouble)msec / 1000;
1019 chat->wakeup_timeout = timeout;
1024 static guint at_chat_send_common(struct at_chat *chat, guint gid,
1026 const char **prefix_list,
1028 GAtNotifyFunc listing,
1031 GDestroyNotify notify)
1033 struct at_command *c;
1035 if (chat == NULL || chat->command_queue == NULL)
1038 c = at_command_create(gid, cmd, prefix_list, flags, listing, func,
1039 user_data, notify, FALSE);
1043 c->id = chat->next_cmd_id++;
1045 g_queue_push_tail(chat->command_queue, c);
1047 if (g_queue_get_length(chat->command_queue) == 1)
1048 chat_wakeup_writer(chat);
1053 static struct at_notify *at_notify_create(struct at_chat *chat,
1057 struct at_notify *notify;
1060 key = g_strdup(prefix);
1064 notify = g_try_new0(struct at_notify, 1);
1065 if (notify == NULL) {
1072 g_hash_table_insert(chat->notify_list, key, notify);
1077 static gboolean at_chat_cancel(struct at_chat *chat, guint group, guint id)
1080 struct at_command *c;
1082 if (chat->command_queue == NULL)
1085 l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
1086 at_command_compare_by_id);
1093 if (c->gid != group)
1096 if (c == g_queue_peek_head(chat->command_queue) &&
1097 chat->cmd_bytes_written > 0) {
1098 /* We can't actually remove it since it is most likely
1099 * already in progress, just null out the callback
1100 * so it won't be called
1104 at_command_destroy(c);
1105 g_queue_remove(chat->command_queue, c);
1111 static gboolean at_chat_cancel_group(struct at_chat *chat, guint group)
1114 struct at_command *c;
1116 if (chat->command_queue == NULL)
1119 while ((c = g_queue_peek_nth(chat->command_queue, n)) != NULL) {
1120 if (c->id == 0 || c->gid != group) {
1125 if (n == 0 && chat->cmd_bytes_written > 0) {
1131 at_command_destroy(c);
1132 g_queue_remove(chat->command_queue, c);
1138 static gpointer at_chat_get_userdata(struct at_chat *chat,
1139 guint group, guint id)
1142 struct at_command *c;
1144 if (chat->command_queue == NULL)
1147 l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
1148 at_command_compare_by_id);
1155 if (c->gid != group)
1158 return c->user_data;
1161 static guint at_chat_register(struct at_chat *chat, guint group,
1162 const char *prefix, GAtNotifyFunc func,
1163 gboolean expect_pdu, gpointer user_data,
1164 GDestroyNotify destroy_notify)
1166 struct at_notify *notify;
1167 struct at_notify_node *node;
1169 if (chat->notify_list == NULL)
1175 if (prefix == NULL || strlen(prefix) == 0)
1178 notify = g_hash_table_lookup(chat->notify_list, prefix);
1181 notify = at_notify_create(chat, prefix, expect_pdu);
1183 if (notify == NULL || notify->pdu != expect_pdu)
1186 node = g_try_new0(struct at_notify_node, 1);
1190 node->id = chat->next_notify_id++;
1192 node->callback = func;
1193 node->user_data = user_data;
1194 node->notify = destroy_notify;
1196 notify->nodes = g_slist_prepend(notify->nodes, node);
1201 static gboolean at_chat_unregister(struct at_chat *chat, gboolean mark_only,
1202 guint group, guint id)
1204 GHashTableIter iter;
1205 struct at_notify *notify;
1206 struct at_notify_node *node;
1207 gpointer key, value;
1210 if (chat->notify_list == NULL)
1213 g_hash_table_iter_init(&iter, chat->notify_list);
1215 while (g_hash_table_iter_next(&iter, &key, &value)) {
1218 l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
1219 at_notify_node_compare_by_id);
1226 if (node->gid != group)
1230 node->destroyed = TRUE;
1234 at_notify_node_destroy(node, NULL);
1235 notify->nodes = g_slist_remove(notify->nodes, node);
1237 if (notify->nodes == NULL)
1238 g_hash_table_iter_remove(&iter);
1246 static gboolean node_compare_by_group(struct at_notify_node *node,
1249 guint group = GPOINTER_TO_UINT(userdata);
1251 if (node->gid == group)
1257 static struct at_chat *create_chat(GIOChannel *channel, GIOFlags flags,
1260 struct at_chat *chat;
1262 if (channel == NULL)
1268 chat = g_try_new0(struct at_chat, 1);
1272 chat->ref_count = 1;
1273 chat->next_cmd_id = 1;
1274 chat->next_notify_id = 1;
1275 chat->debugf = NULL;
1277 if (flags & G_IO_FLAG_NONBLOCK)
1278 chat->io = g_at_io_new(channel);
1280 chat->io = g_at_io_new_blocking(channel);
1282 if (chat->io == NULL)
1285 g_at_io_set_disconnect_function(chat->io, io_disconnect, chat);
1287 chat->command_queue = g_queue_new();
1288 if (chat->command_queue == NULL)
1291 chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal,
1292 g_free, at_notify_destroy);
1294 g_at_io_set_read_handler(chat->io, new_bytes, chat);
1296 chat->syntax = g_at_syntax_ref(syntax);
1301 g_at_io_unref(chat->io);
1303 if (chat->command_queue)
1304 g_queue_free(chat->command_queue);
1306 if (chat->notify_list)
1307 g_hash_table_destroy(chat->notify_list);
1313 static GAtChat *g_at_chat_new_common(GIOChannel *channel, GIOFlags flags,
1318 chat = g_try_new0(GAtChat, 1);
1322 chat->parent = create_chat(channel, flags, syntax);
1323 if (chat->parent == NULL) {
1328 chat->group = chat->parent->next_gid++;
1329 chat->ref_count = 1;
1334 GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax)
1336 return g_at_chat_new_common(channel, G_IO_FLAG_NONBLOCK, syntax);
1339 GAtChat *g_at_chat_new_blocking(GIOChannel *channel, GAtSyntax *syntax)
1341 return g_at_chat_new_common(channel, 0, syntax);
1344 GAtChat *g_at_chat_clone(GAtChat *clone)
1351 chat = g_try_new0(GAtChat, 1);
1355 chat->parent = clone->parent;
1356 chat->group = chat->parent->next_gid++;
1357 chat->ref_count = 1;
1358 g_atomic_int_inc(&chat->parent->ref_count);
1360 if (clone->slave != NULL)
1361 chat->slave = g_at_chat_clone(clone->slave);
1366 GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave)
1371 if (chat->slave != NULL)
1372 g_at_chat_unref(chat->slave);
1375 chat->slave = g_at_chat_ref(slave);
1382 GAtChat *g_at_chat_get_slave(GAtChat *chat)
1390 GIOChannel *g_at_chat_get_channel(GAtChat *chat)
1392 if (chat == NULL || chat->parent->io == NULL)
1395 return g_at_io_get_channel(chat->parent->io);
1398 GAtIO *g_at_chat_get_io(GAtChat *chat)
1403 return chat->parent->io;
1406 GAtChat *g_at_chat_ref(GAtChat *chat)
1411 g_atomic_int_inc(&chat->ref_count);
1416 void g_at_chat_suspend(GAtChat *chat)
1421 at_chat_suspend(chat->parent);
1424 void g_at_chat_resume(GAtChat *chat)
1429 at_chat_resume(chat->parent);
1432 void g_at_chat_unref(GAtChat *chat)
1439 is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
1441 if (is_zero == FALSE)
1444 if (chat->slave != NULL)
1445 g_at_chat_unref(chat->slave);
1447 at_chat_cancel_group(chat->parent, chat->group);
1448 g_at_chat_unregister_all(chat);
1449 at_chat_unref(chat->parent);
1454 gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
1455 GAtDisconnectFunc disconnect, gpointer user_data)
1457 if (chat == NULL || chat->group != 0)
1460 return at_chat_set_disconnect_function(chat->parent, disconnect,
1464 gboolean g_at_chat_set_debug(GAtChat *chat,
1465 GAtDebugFunc func, gpointer user_data)
1468 if (chat == NULL || chat->group != 0)
1471 return at_chat_set_debug(chat->parent, func, user_data);
1474 void g_at_chat_add_terminator(GAtChat *chat, char *terminator,
1475 int len, gboolean success)
1477 if (chat == NULL || chat->group != 0)
1480 at_chat_add_terminator(chat->parent, terminator, len, success);
1483 void g_at_chat_blacklist_terminator(GAtChat *chat,
1484 GAtChatTerminator terminator)
1486 if (chat == NULL || chat->group != 0)
1489 at_chat_blacklist_terminator(chat->parent, terminator);
1492 gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
1493 unsigned int timeout, unsigned int msec)
1495 if (chat == NULL || chat->group != 0)
1498 return at_chat_set_wakeup_command(chat->parent, cmd, timeout, msec);
1501 guint g_at_chat_send(GAtChat *chat, const char *cmd,
1502 const char **prefix_list, GAtResultFunc func,
1503 gpointer user_data, GDestroyNotify notify)
1505 return at_chat_send_common(chat->parent, chat->group,
1506 cmd, prefix_list, 0, NULL,
1507 func, user_data, notify);
1510 guint g_at_chat_send_listing(GAtChat *chat, const char *cmd,
1511 const char **prefix_list,
1512 GAtNotifyFunc listing, GAtResultFunc func,
1513 gpointer user_data, GDestroyNotify notify)
1515 if (listing == NULL)
1518 return at_chat_send_common(chat->parent, chat->group,
1519 cmd, prefix_list, 0,
1520 listing, func, user_data, notify);
1523 guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd,
1524 const char **prefix_list,
1525 GAtNotifyFunc listing, GAtResultFunc func,
1526 gpointer user_data, GDestroyNotify notify)
1528 if (listing == NULL)
1531 return at_chat_send_common(chat->parent, chat->group,
1533 COMMAND_FLAG_EXPECT_PDU,
1534 listing, func, user_data, notify);
1537 guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd,
1538 const char **prefix_list,
1541 GDestroyNotify notify)
1543 return at_chat_send_common(chat->parent, chat->group,
1545 COMMAND_FLAG_EXPECT_SHORT_PROMPT,
1546 NULL, func, user_data, notify);
1549 gboolean g_at_chat_cancel(GAtChat *chat, guint id)
1551 /* We use id 0 for wakeup commands */
1552 if (chat == NULL || id == 0)
1555 return at_chat_cancel(chat->parent, chat->group, id);
1558 gboolean g_at_chat_cancel_all(GAtChat *chat)
1563 return at_chat_cancel_group(chat->parent, chat->group);
1566 gpointer g_at_chat_get_userdata(GAtChat *chat, guint id)
1571 return at_chat_get_userdata(chat->parent, chat->group, id);
1574 guint g_at_chat_register(GAtChat *chat, const char *prefix,
1575 GAtNotifyFunc func, gboolean expect_pdu,
1577 GDestroyNotify destroy_notify)
1582 return at_chat_register(chat->parent, chat->group, prefix,
1583 func, expect_pdu, user_data, destroy_notify);
1586 gboolean g_at_chat_unregister(GAtChat *chat, guint id)
1591 return at_chat_unregister(chat->parent, chat->parent->in_notify,
1595 gboolean g_at_chat_unregister_all(GAtChat *chat)
1600 return at_chat_unregister_all(chat->parent,
1601 chat->parent->in_notify,
1602 node_compare_by_group,
1603 GUINT_TO_POINTER(chat->group));