diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/main.c | 23 | ||||
-rw-r--r-- | src/mm-callback-info.c | 16 | ||||
-rw-r--r-- | src/mm-callback-info.h | 2 | ||||
-rw-r--r-- | src/mm-errors.c | 175 | ||||
-rw-r--r-- | src/mm-errors.h | 110 | ||||
-rw-r--r-- | src/mm-generic-cdma.c | 118 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 737 | ||||
-rw-r--r-- | src/mm-manager.c | 8 | ||||
-rw-r--r-- | src/mm-modem-cdma.c | 2 | ||||
-rw-r--r-- | src/mm-modem-error.c | 37 | ||||
-rw-r--r-- | src/mm-modem-error.h | 22 | ||||
-rw-r--r-- | src/mm-modem-gsm-card.c | 2 | ||||
-rw-r--r-- | src/mm-modem-gsm-network.c | 2 | ||||
-rw-r--r-- | src/mm-modem.c | 2 | ||||
-rw-r--r-- | src/mm-options.c | 36 | ||||
-rw-r--r-- | src/mm-options.h | 9 | ||||
-rw-r--r-- | src/mm-serial-parsers.c | 266 | ||||
-rw-r--r-- | src/mm-serial-parsers.h | 26 | ||||
-rw-r--r-- | src/mm-serial.c | 708 | ||||
-rw-r--r-- | src/mm-serial.h | 91 |
21 files changed, 1224 insertions, 1178 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9240d9f4..f0204704 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,22 +15,26 @@ modem_manager_SOURCES = \ mm-generic-cdma.h \ mm-generic-gsm.c \ mm-generic-gsm.h \ + mm-errors.c \ + mm-errors.h \ mm-manager.c \ mm-manager.h \ mm-modem.c \ mm-modem.h \ mm-modem-cdma.c \ mm-modem-cdma.h \ - mm-modem-error.c \ - mm-modem-error.h \ mm-modem-gsm-card.c \ mm-modem-gsm-card.h \ mm-modem-gsm-network.c \ mm-modem-gsm-network.h \ + mm-options.c \ + mm-options.h \ mm-plugin.c \ mm-plugin.h \ mm-serial.c \ - mm-serial.h + mm-serial.h \ + mm-serial-parsers.c \ + mm-serial-parsers.h mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< @@ -3,6 +3,7 @@ #include <syslog.h> #include <dbus/dbus-glib.h> #include "mm-manager.h" +#include "mm-options.h" static void log_handler (const gchar *log_domain, @@ -123,31 +124,13 @@ dbus_init (GMainLoop *loop) int main (int argc, char *argv[]) { - GOptionContext *opt_ctx; - GError *error = NULL; GMainLoop *loop; MMManager *manager; - gboolean debug = FALSE; - GOptionEntry entries[] = { - { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, - { NULL } - }; - - opt_ctx = g_option_context_new (NULL); - g_option_context_set_summary (opt_ctx, "DBus system service to communicate with modems."); - g_option_context_add_main_entries (opt_ctx, entries, NULL); - - if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { - g_warning ("%s\n", error->message); - g_error_free (error); - return 1; - } - - g_option_context_free (opt_ctx); + mm_options_parse (argc, argv); g_type_init (); - if (!debug) + if (!mm_options_debug ()) logging_setup (); loop = g_main_loop_new (NULL, FALSE); diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c index d016aad4..61283b26 100644 --- a/src/mm-callback-info.c +++ b/src/mm-callback-info.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ #include "mm-callback-info.h" -#include "mm-modem-error.h" +#include "mm-errors.h" static void callback_info_done (gpointer user_data) @@ -23,9 +23,10 @@ callback_info_done (gpointer user_data) if (info->error) g_error_free (info->error); - g_object_unref (info->modem); - g_datalist_clear (&info->qdata); + if (info->modem) + g_object_unref (info->modem); + g_datalist_clear (&info->qdata); g_slice_free (MMCallbackInfo, info); } @@ -43,15 +44,6 @@ mm_callback_info_schedule (MMCallbackInfo *info) info->pending_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, callback_info_do, info, callback_info_done); } -void -mm_callback_info_cancel (MMCallbackInfo *info) -{ - if (info->pending_id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Call cancelled"); - mm_callback_info_schedule (info); - } -} - MMCallbackInfo * mm_callback_info_new (MMModem *modem, MMModemFn callback, gpointer user_data) { diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h index eef2073f..5c461c58 100644 --- a/src/mm-callback-info.h +++ b/src/mm-callback-info.h @@ -31,8 +31,6 @@ MMCallbackInfo *mm_callback_info_string_new (MMModem *modem, gpointer user_data); void mm_callback_info_schedule (MMCallbackInfo *info); -void mm_callback_info_cancel (MMCallbackInfo *info); - void mm_callback_info_set_result (MMCallbackInfo *info, gpointer data, GDestroyNotify destroy); diff --git a/src/mm-errors.c b/src/mm-errors.c new file mode 100644 index 00000000..bdbd8a80 --- /dev/null +++ b/src/mm-errors.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "mm-errors.h" + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GQuark +mm_serial_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_serial_error"); + + return ret; +} + +GType +mm_serial_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_SERIAL_OPEN_FAILED, "Could not open the serial device"), + ENUM_ENTRY (MM_SERIAL_SEND_FAILED, "Writing to serial device failed"), + ENUM_ENTRY (MM_SERIAL_RESPONSE_TIMEOUT, "Did not receive response"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMSerialError", values); + } + + return etype; +} + +GQuark +mm_modem_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_modem_error"); + + return ret; +} + +GType +mm_modem_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "Unknown error"), + ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "Operation not supported"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMModemError", values); + } + + return etype; +} + +GQuark +mm_modem_connect_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_modem_connect_error"); + + return ret; +} + +GType +mm_modem_connect_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_CARRIER, "No carrier"), + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_DIALTONE, "No dialtone"), + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_BUSY, "Busy"), + ENUM_ENTRY (MM_MODEM_CONNECT_ERROR_NO_ANSWER, "No answer"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMModemConnectError", values); + } + + return etype; +} + +GQuark +mm_mobile_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) + ret = g_quark_from_static_string ("mm_mobile_error"); + + return ret; +} + +GType +mm_mobile_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (MM_MOBILE_ERROR_PHONE_FAILURE, "PhoneFailure"), + ENUM_ENTRY (MM_MOBILE_ERROR_NO_CONNECTION, "No connection to phone"), + ENUM_ENTRY (MM_MOBILE_ERROR_LINK_RESERVED, "Phone-adaptor link reserved"), + ENUM_ENTRY (MM_MOBILE_ERROR_NOT_ALLOWED, "Operation not allowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_NOT_SUPPORTED, "Operation not supported"), + ENUM_ENTRY (MM_MOBILE_ERROR_PH_SIM_PIN, "PH-SIM PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_PH_FSIM_PIN, "PH-FSIM PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_PH_FSIM_PUK, "PH-FSIM PUK required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_NOT_INSERTED, "SIM not inserted"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PIN, "SIM PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PUK, "SIM PUK required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_FAILURE, "SIM failure"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_BUSY, "SIM busy"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_WRONG, "SIM wrong"), + ENUM_ENTRY (MM_MOBILE_ERROR_WRONG_PASSWORD, "Incorrect password"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PIN2, "SIM PIN2 required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SIM_PUK2, "SIM PUK2 required"), + ENUM_ENTRY (MM_MOBILE_ERROR_MEMORY_FULL, "Memory full"), + ENUM_ENTRY (MM_MOBILE_ERROR_INVALID_INDEX, "Invalid index"), + ENUM_ENTRY (MM_MOBILE_ERROR_NOT_FOUND, "Not found"), + ENUM_ENTRY (MM_MOBILE_ERROR_MEMORY_FAILURE, "Memory failure"), + ENUM_ENTRY (MM_MOBILE_ERROR_TEXT_TOO_LONG, "Text string too long"), + ENUM_ENTRY (MM_MOBILE_ERROR_INVALID_CHARS, "Invalid charactes in text string"), + ENUM_ENTRY (MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG, "Dial string too long"), + ENUM_ENTRY (MM_MOBILE_ERROR_DIAL_STRING_INVALID, "Invalid charactes in dial string"), + ENUM_ENTRY (MM_MOBILE_ERROR_NO_NETWORK, "No network service"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_TIMEOUT, "Network timeout"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED, "Network not allowed - emergency calls only"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_PIN, "Network personalization PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_PUK, "Network personalization PUK required"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_SUBSET_PIN, "Network subset personalization PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_NETWORK_SUBSET_PUK, "Network subset personalization PUK required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SERVICE_PIN, "Service provider personalization PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_SERVICE_PUK, "Service provider personalization PUK required"), + ENUM_ENTRY (MM_MOBILE_ERROR_CORP_PIN, "Corporate personalization PIN required"), + ENUM_ENTRY (MM_MOBILE_ERROR_CORP_PUK, "Corporate personalization PUK required"), + ENUM_ENTRY (MM_MOBILE_ERROR_HIDDEN_KEY, "Hidden key required"), + ENUM_ENTRY (MM_MOBILE_ERROR_EAP_NOT_SUPPORTED, "EAP method not supported"), + ENUM_ENTRY (MM_MOBILE_ERROR_INCORRECT_PARAMS, "Incorrect parameters"), + ENUM_ENTRY (MM_MOBILE_ERROR_UNKNOWN, "Unknown"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ILLEGAL_MS, "Illegal MS"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ILLEGAL_ME, "Illegal ME"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED, "GPRS services not allowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED, "PLMN not allowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED, "Location area not allowed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED, "Roaming not allowed in this location area"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED, "Service option not supported"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED, "Requested service option not subscribed"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER, "Service option temporarily out of order"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE, "PDP authentication failure"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_UNKNOWN, "Unspecified GPRS error"), + ENUM_ENTRY (MM_MOBILE_ERROR_GPRS_INVALID_CLASS, "Invalid mobile class"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("MMMobileError", values); + g_print ("Is enum? %d %d\n", etype, G_TYPE_IS_ENUM (etype)); + } + + return etype; +} diff --git a/src/mm-errors.h b/src/mm-errors.h new file mode 100644 index 00000000..6d3ca93a --- /dev/null +++ b/src/mm-errors.h @@ -0,0 +1,110 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef MM_MODEM_ERROR_H +#define MM_MODEM_ERROR_H + +#include <glib-object.h> + +enum { + MM_SERIAL_OPEN_FAILED = 0, + MM_SERIAL_SEND_FAILED = 1, + MM_SERIAL_RESPONSE_TIMEOUT = 2 +}; + +#define MM_SERIAL_ERROR (mm_serial_error_quark ()) +#define MM_TYPE_SERIAL_ERROR (mm_serial_error_get_type ()) + +GQuark mm_serial_error_quark (void); +GType mm_serial_error_get_type (void); + + +enum { + MM_MODEM_ERROR_GENERAL = 0, + MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED = 1 +}; + +#define MM_MODEM_ERROR (mm_modem_error_quark ()) +#define MM_TYPE_MODEM_ERROR (mm_modem_error_get_type ()) + +GQuark mm_modem_error_quark (void); +GType mm_modem_error_get_type (void); + + +enum { + MM_MODEM_CONNECT_ERROR_NO_CARRIER = 3, + MM_MODEM_CONNECT_ERROR_NO_DIALTONE = 6, + MM_MODEM_CONNECT_ERROR_BUSY = 7, + MM_MODEM_CONNECT_ERROR_NO_ANSWER = 8, +}; + +#define MM_MODEM_CONNECT_ERROR (mm_modem_connect_error_quark ()) +#define MM_TYPE_MODEM_CONNECT_ERROR (mm_modem_connect_error_get_type ()) + +GQuark mm_modem_connect_error_quark (void); +GType mm_modem_connect_error_get_type (void); + + +enum { + MM_MOBILE_ERROR_PHONE_FAILURE = 0, + MM_MOBILE_ERROR_NO_CONNECTION = 1, + MM_MOBILE_ERROR_LINK_RESERVED = 2, + MM_MOBILE_ERROR_NOT_ALLOWED = 3, + MM_MOBILE_ERROR_NOT_SUPPORTED = 4, + MM_MOBILE_ERROR_PH_SIM_PIN = 5, + MM_MOBILE_ERROR_PH_FSIM_PIN = 6, + MM_MOBILE_ERROR_PH_FSIM_PUK = 7, + MM_MOBILE_ERROR_SIM_NOT_INSERTED = 10, + MM_MOBILE_ERROR_SIM_PIN = 11, + MM_MOBILE_ERROR_SIM_PUK = 12, + MM_MOBILE_ERROR_SIM_FAILURE = 13, + MM_MOBILE_ERROR_SIM_BUSY = 14, + MM_MOBILE_ERROR_SIM_WRONG = 15, + MM_MOBILE_ERROR_WRONG_PASSWORD = 16, + MM_MOBILE_ERROR_SIM_PIN2 = 17, + MM_MOBILE_ERROR_SIM_PUK2 = 18, + MM_MOBILE_ERROR_MEMORY_FULL = 20, + MM_MOBILE_ERROR_INVALID_INDEX = 21, + MM_MOBILE_ERROR_NOT_FOUND = 22, + MM_MOBILE_ERROR_MEMORY_FAILURE = 23, + MM_MOBILE_ERROR_TEXT_TOO_LONG = 24, + MM_MOBILE_ERROR_INVALID_CHARS = 25, + MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG = 26, + MM_MOBILE_ERROR_DIAL_STRING_INVALID = 27, + MM_MOBILE_ERROR_NO_NETWORK = 30, + MM_MOBILE_ERROR_NETWORK_TIMEOUT = 31, + MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED = 32, + MM_MOBILE_ERROR_NETWORK_PIN = 40, + MM_MOBILE_ERROR_NETWORK_PUK = 41, + MM_MOBILE_ERROR_NETWORK_SUBSET_PIN = 42, + MM_MOBILE_ERROR_NETWORK_SUBSET_PUK = 43, + MM_MOBILE_ERROR_SERVICE_PIN = 44, + MM_MOBILE_ERROR_SERVICE_PUK = 45, + MM_MOBILE_ERROR_CORP_PIN = 46, + MM_MOBILE_ERROR_CORP_PUK = 47, + MM_MOBILE_ERROR_HIDDEN_KEY = 48, + MM_MOBILE_ERROR_EAP_NOT_SUPPORTED = 49, + MM_MOBILE_ERROR_INCORRECT_PARAMS = 50, + MM_MOBILE_ERROR_UNKNOWN = 100, + + MM_MOBILE_ERROR_GPRS_ILLEGAL_MS = 103, + MM_MOBILE_ERROR_GPRS_ILLEGAL_ME = 106, + MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED = 107, + MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED = 111, + MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED = 112, + MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED = 113, + MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED = 132, + MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED = 133, + MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER = 134, + MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149, + MM_MOBILE_ERROR_GPRS_UNKNOWN = 148, + MM_MOBILE_ERROR_GPRS_INVALID_CLASS = 150 +}; + + +#define MM_MOBILE_ERROR (mm_mobile_error_quark ()) +#define MM_TYPE_MOBILE_ERROR (mm_mobile_error_get_type ()) + +GQuark mm_mobile_error_quark (void); +GType mm_mobile_error_get_type (void); + +#endif /* MM_MODEM_ERROR_H */ diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 9167f418..fe49edac 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -5,7 +5,7 @@ #include "mm-generic-cdma.h" #include "mm-modem-cdma.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-callback-info.h" static gpointer mm_generic_cdma_parent_class = NULL; @@ -32,21 +32,14 @@ mm_generic_cdma_new (const char *serial_device, const char *driver) static void init_done (MMSerial *serial, - int reply_index, + GString *response, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization timed out."); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization failed"); - } + if (error) + info->error = g_error_copy (error); mm_callback_info_schedule (info); } @@ -54,18 +47,7 @@ init_done (MMSerial *serial, static void flash_done (MMSerial *serial, gpointer user_data) { - char *responses[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; - - if (mm_serial_send_command_string (serial, "AT E0")) - id = mm_serial_wait_for_reply (serial, 10, responses, responses, init_done, user_data); - - if (!id) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Turning modem echo off failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (serial, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data); } static void @@ -84,15 +66,8 @@ enable (MMModem *modem, return; } - if (mm_serial_open (MM_SERIAL (modem))) { - guint id; - - id = mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info); - if (!id) - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "%s", "Could not communicate with serial device."); - } else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not open serial device."); + if (mm_serial_open (MM_SERIAL (modem), &info->error)) + mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info); if (info->error) mm_callback_info_schedule (info); @@ -100,31 +75,14 @@ enable (MMModem *modem, static void dial_done (MMSerial *serial, - int reply_index, + GString *response, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ - break; - case 1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: Busy"); - break; - case 2: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No dial tone"); - break; - case 3: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No carrier"); - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing timed out"); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed"); - break; - } + if (error) + info->error = g_error_copy (error); mm_callback_info_schedule (info); } @@ -137,21 +95,17 @@ connect (MMModem *modem, { MMCallbackInfo *info; char *command; - char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL }; - guint id = 0; info = mm_callback_info_new (modem, callback, user_data); - - command = g_strconcat ("ATDT", number, NULL); - if (mm_serial_send_command_string (MM_SERIAL (modem), command)) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, responses, dial_done, info); - + command = g_strconcat ("DT", number, NULL); + mm_serial_queue_command (MM_SERIAL (modem), command, 60, dial_done, info); g_free (command); +} - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed."); - mm_callback_info_schedule (info); - } +static void +disconnect_flash_done (MMSerial *serial, gpointer user_data) +{ + mm_callback_info_schedule ((MMCallbackInfo *) user_data); } static void @@ -162,17 +116,21 @@ disconnect (MMModem *modem, MMCallbackInfo *info; info = mm_callback_info_new (modem, callback, user_data); - mm_serial_close (MM_SERIAL (modem)); - mm_callback_info_schedule (info); + mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info); } static void -get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data) +get_signal_quality_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - guint32 result = 0; + char *reply = response->str; - if (!strncmp (reply, "+CSQ: ", 6)) { + if (error) + info->error = g_error_copy (error); + else if (!strncmp (reply, "+CSQ: ", 6)) { /* Got valid reply */ int quality; int ber; @@ -183,15 +141,14 @@ get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data /* 99 means unknown */ if (quality != 99) /* Normalize the quality */ - result = quality * 100 / 31; + quality = quality * 100 / 31; + + mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); } else info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse signal quality results"); - } else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "%s", "Could not parse signal quality results"); + } - mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL); mm_callback_info_schedule (info); } @@ -201,18 +158,9 @@ get_signal_quality (MMModemCdma *modem, gpointer user_data) { MMCallbackInfo *info; - char *terminators = "\r\n"; - guint id = 0; info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CSQ")) - id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, get_signal_quality_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Getting signal quality failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info); } /*****************************************************************************/ diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index f7b7a178..88589ef7 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -6,8 +6,9 @@ #include "mm-generic-gsm.h" #include "mm-modem-gsm-card.h" #include "mm-modem-gsm-network.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-callback-info.h" +#include "mm-serial-parsers.h" static gpointer mm_generic_gsm_parent_class = NULL; @@ -22,7 +23,8 @@ typedef struct { guint32 pending_id; } MMGenericGsmPrivate; -static void register_auto (MMModemGsmNetwork *modem, MMCallbackInfo *info); +static void get_registration_status (MMSerial *serial, MMCallbackInfo *info); +static void real_register (MMSerial *serial, const char *network_id, MMCallbackInfo *info); MMModem * mm_generic_gsm_new (const char *serial_device, const char *driver) @@ -74,89 +76,23 @@ mm_generic_gsm_set_operator (MMGenericGsm *modem, /*****************************************************************************/ static void -check_pin_done (MMSerial *serial, - int reply_index, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - switch (reply_index) { - case 0: - /* success */ - break; - case 1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_PIN_NEEDED, "%s", "PIN needed"); - break; - case 2: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_PUK_NEEDED, "%s", "PUK needed"); - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking timed out."); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking failed."); - break; - } - - mm_callback_info_schedule (info); -} - -static void -check_pin (MMSerial *serial, gpointer user_data) -{ - char *responses[] = { "READY", "SIM PIN", "SIM PUK", "ERROR", "ERR", NULL }; - char *terminators[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; - - if (mm_serial_send_command_string (serial, "AT+CPIN?")) - id = mm_serial_wait_for_reply (serial, 3, responses, terminators, check_pin_done, user_data); - - if (!id) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking failed."); - mm_callback_info_schedule (info); - } -} - -static void init_done (MMSerial *serial, - int reply_index, + GString *response, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ - check_pin (serial, user_data); - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization timed out."); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Modem initialization failed"); - } + if (error) + info->error = g_error_copy (error); - if (info->error) - mm_callback_info_schedule (info); + mm_callback_info_schedule (info); } static void flash_done (MMSerial *serial, gpointer user_data) { - char *responses[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; - - if (mm_serial_send_command_string (serial, "ATZ E0")) - id = mm_serial_wait_for_reply (serial, 10, responses, responses, init_done, user_data); - - if (!id) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Turning modem echo off failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (serial, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data); } static void @@ -175,29 +111,25 @@ enable (MMModem *modem, return; } - if (mm_serial_open (MM_SERIAL (modem))) { - guint id; - - id = mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info); - if (!id) - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "%s", "Could not communicate with serial device."); - } else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not open serial device."); + if (mm_serial_open (MM_SERIAL (modem), &info->error)) + mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info); if (info->error) mm_callback_info_schedule (info); } static void -get_string_done (MMSerial *serial, const char *reply, gpointer user_data) +get_string_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - if (reply) - mm_callback_info_set_result (info, g_strdup (reply), g_free); + if (error) + info->error = g_error_copy (error); else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading information failed."); + mm_callback_info_set_result (info, g_strdup (response->str), g_free); mm_callback_info_schedule (info); } @@ -208,18 +140,9 @@ get_imei (MMModemGsmCard *modem, gpointer user_data) { MMCallbackInfo *info; - const char *terminators = "\r\n"; - guint id = 0; info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CGSN")) - id = mm_serial_get_reply (MM_SERIAL (modem), 3, terminators, get_string_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading IMEI failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (MM_SERIAL (modem), "+CGSN", 3, get_string_done, info); } static void @@ -228,21 +151,11 @@ get_imsi (MMModemGsmCard *modem, gpointer user_data) { MMCallbackInfo *info; - const char *terminators = "\r\n"; - guint id = 0; info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CIMI")) - id = mm_serial_get_reply (MM_SERIAL (modem), 3, terminators, get_string_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading IMSI failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (MM_SERIAL (modem), "+CIMI", 3, get_string_done, info); } - static void card_info_wrapper (MMModem *modem, GError *error, @@ -263,52 +176,47 @@ card_info_wrapper (MMModem *modem, } static void -get_version_done (MMSerial *serial, const char *reply, gpointer user_data) +get_version_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - if (reply) - mm_callback_info_set_data (info, "card-info-version", g_strdup (reply), g_free); - else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading version failed."); - + if (!error) + mm_callback_info_set_data (info, "card-info-version", g_strdup (response->str), g_free); + else if (!info->error) + info->error = g_error_copy (error); + mm_callback_info_schedule (info); } static void -get_model_done (MMSerial *serial, const char *reply, gpointer user_data) +get_model_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *terminators = "\r\n"; - guint id = 0; - - if (reply && mm_serial_send_command_string (serial, "AT+CGMR")) - id = mm_serial_get_reply (serial, 5, terminators, get_version_done, info); - if (id) - mm_callback_info_set_data (info, "card-info-model", g_strdup (reply), g_free); - else { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading model failed."); - mm_callback_info_schedule (info); - } + if (!error) + mm_callback_info_set_data (info, "card-info-model", g_strdup (response->str), g_free); + else if (!info->error) + info->error = g_error_copy (error); } static void -get_manufacturer_done (MMSerial *serial, const char *reply, gpointer user_data) +get_manufacturer_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *terminators = "\r\n"; - guint id = 0; - if (reply && mm_serial_send_command_string (serial, "AT+CGMM")) - id = mm_serial_get_reply (serial, 5, terminators, get_model_done, info); - - if (id) - mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (reply), g_free); - else { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading manufacturer failed."); - mm_callback_info_schedule (info); - } + if (!error) + mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (response->str), g_free); + else + info->error = g_error_copy (error); } static void @@ -317,43 +225,27 @@ get_card_info (MMModemGsmCard *modem, gpointer user_data) { MMCallbackInfo *info; - char *terminators = "\r\n"; - guint id = 0; info = mm_callback_info_new (MM_MODEM (modem), card_info_wrapper, NULL); info->user_data = info; mm_callback_info_set_data (info, "card-info-callback", callback, NULL); mm_callback_info_set_data (info, "card-info-data", user_data, NULL); - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CGMI")) - id = mm_serial_get_reply (MM_SERIAL (modem), 5, terminators, get_manufacturer_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading card information failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (MM_SERIAL (modem), "+CGMI", 3, get_manufacturer_done, info); + mm_serial_queue_command (MM_SERIAL (modem), "+CGMM", 3, get_model_done, info); + mm_serial_queue_command (MM_SERIAL (modem), "+CGMR", 3, get_version_done, info); } static void send_pin_done (MMSerial *serial, - int reply_index, + GString *response, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "%s", "Did not receive response for secret"); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_INVALID_SECRET, "%s", "Invalid secret"); - break; - } - + if (error) + info->error = g_error_copy (error); mm_callback_info_schedule (info); } @@ -365,21 +257,11 @@ send_pin (MMModemGsmCard *modem, { MMCallbackInfo *info; char *command; - char *responses[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); - - command = g_strdup_printf ("AT+CPIN=\"%s\"", pin); - if (mm_serial_send_command_string (MM_SERIAL (modem), command)) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 3, responses, responses, send_pin_done, info); - + command = g_strdup_printf ("+CPIN=\"%s\"", pin); + mm_serial_queue_command (MM_SERIAL (modem), command, 3, send_pin_done, info); g_free (command); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "PIN checking failed."); - mm_callback_info_schedule (info); - } } static char * @@ -409,244 +291,206 @@ parse_operator (const char *reply) } static void -get_reg_name_done (MMSerial *serial, const char *reply, gpointer user_data) +get_reg_code_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *oper; + const char *reply = response->str; - oper = parse_operator (reply); - if (!oper) - g_warning ("Could not parse operator"); + if (!error) { + char *oper; - mm_generic_gsm_set_operator (MM_GENERIC_GSM (serial), - (char *) mm_callback_info_get_data (info, "reg-info-oper-code"), - oper); - g_free (oper); + oper = parse_operator (reply); + if (oper) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial); - mm_callback_info_schedule (info); + g_free (priv->oper_code); + priv->oper_name = oper; + } + } } static void -get_reg_code_done (MMSerial *serial, const char *reply, gpointer user_data) +get_reg_name_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *oper; - guint id = 0; + const char *reply = response->str; - oper = parse_operator (reply); - if (oper) { - char *terminators = "\r\n"; + if (!error) { + char *oper; - mm_callback_info_set_data (info, "reg-info-oper-code", oper, g_free); + oper = parse_operator (reply); + if (oper) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial); - if (mm_serial_send_command_string (serial, "AT+COPS=3,0;+COPS?")) - id = mm_serial_get_reply (MM_SERIAL (serial), 5, terminators, get_reg_name_done, info); + g_free (priv->oper_name); + priv->oper_name = oper; + } } +} - if (!id) { - g_warning ("Could not read operator"); +static gboolean +reg_status_again (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + guint32 counter; + + counter = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "reg-status-counter")); + if (counter > 60) { + /* That's 60 seconds */ + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NETWORK_TIMEOUT, + "Registration timed out"); mm_callback_info_schedule (info); + } else { + mm_callback_info_set_data (info, "reg-status-counter", + GUINT_TO_POINTER (++counter), NULL); + get_registration_status (MM_SERIAL (info->modem), info); } + + return TRUE; } static void -read_operator (MMGenericGsm *modem, - MMCallbackInfo *info) +reg_status_remove (gpointer data) { - char *terminators = "\r\n"; - guint id = 0; - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+COPS=3,2;+COPS?")) - id = mm_serial_get_reply (MM_SERIAL (modem), 5, terminators, get_reg_code_done, info); - - if (!id) { - g_warning ("Reading operator code failed."); - mm_callback_info_schedule (info); - } + g_source_remove (GPOINTER_TO_UINT (data)); } static void get_reg_status_done (MMSerial *serial, - int reply_index, + GString *response, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMModemGsmNetworkRegStatus status; + const char *reply = response->str; + guint32 id; + gboolean done = FALSE; - switch (reply_index) { - case 0: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE; - break; - case 1: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME; - break; - case 2: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING; - break; - case 3: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED; - break; - case 4: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING; - break; - case -1: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", - "Reading registration status timed out"); - break; - default: - status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", - "Reading registration status failed"); - break; + if (error) { + info->error = g_error_copy (error); + goto out; } - mm_generic_gsm_set_reg_status (MM_GENERIC_GSM (serial), status); - mm_callback_info_set_result (info, GUINT_TO_POINTER (status), NULL); - - mm_callback_info_schedule (info); -} - -static void -get_registration_status (MMModemGsmNetwork *modem, MMModemUIntFn callback, gpointer user_data) -{ - MMCallbackInfo *info; - char *responses[] = { "+CREG: 0,0", "+CREG: 0,1", "+CREG: 0,2", "+CREG: 0,3", "+CREG: 0,5", NULL }; - char *terminators[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; - - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CREG?")) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, terminators, - get_reg_status_done, info); + if (g_str_has_prefix (reply, "+CREG: ")) { + /* Got valid reply */ + int n, stat; + + if (sscanf (reply + 7, "%d,%d", &n, &stat)) { + MMModemGsmNetworkRegStatus status; + + switch (stat) { + case 0: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE; + break; + case 1: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME; + break; + case 2: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING; + break; + case 3: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED; + break; + case 4: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; + break; + case 5: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING; + break; + default: + status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; + break; + } + + mm_generic_gsm_set_reg_status (MM_GENERIC_GSM (serial), status); + + switch (status) { + case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME: + case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING: + /* Done */ + mm_serial_queue_command (serial, "+COPS=3,2;+COPS?", 3, get_reg_code_done, NULL); + mm_serial_queue_command (serial, "+COPS=3,0;+COPS?", 3, get_reg_name_done, NULL); + done = TRUE; + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE: + /* Huh? Stupid card, we already told it to register, tell again */ + real_register (serial, + (char *) mm_callback_info_get_data (info, "reg-network-id"), + info); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING: + /* Wait more until the timeout expires. */ + id = GPOINTER_TO_INT (mm_callback_info_get_data (info, "reg-status-timeout")); + if (!id) { + id = g_timeout_add (1000, reg_status_again, info); + mm_callback_info_set_data (info, "reg-status-timeout", GUINT_TO_POINTER (id), + reg_status_remove); + } + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED: + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED, + "Network no allowed"); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN: + default: + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown network status"); + break; + } + } + } else + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse the response"); - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Reading registration status failed."); + out: + if (done || info->error) mm_callback_info_schedule (info); - } } static void -register_manual_get_status_done (MMModem *modem, - guint32 result, - GError *error, - gpointer user_data) +get_registration_status (MMSerial *serial, MMCallbackInfo *info) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - if (result == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME || result == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) - read_operator (MM_GENERIC_GSM (modem), info); - else - mm_callback_info_schedule (info); + g_debug ("Queueing +CREG"); + mm_serial_queue_command (serial, "+CREG?", 3, get_reg_status_done, info); } static void -register_manual_done (MMSerial *serial, - int reply_index, - gpointer user_data) +register_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ - get_registration_status (MM_MODEM_GSM_NETWORK (serial), register_manual_get_status_done, info); - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Manual registration timed out"); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Manual registration failed"); - break; - } - - if (info->error) + if (error) { + info->error = g_error_copy (error); mm_callback_info_schedule (info); + } else + get_registration_status (serial, info); } static void -register_manual (MMModemGsmNetwork *modem, const char *network_id, MMCallbackInfo *info) +real_register (MMSerial *serial, + const char *network_id, + MMCallbackInfo *info) { char *command; - char *responses[] = { "OK", "ERROR", "ERR", NULL }; - guint id = 0; - - command = g_strdup_printf ("AT+COPS=1,2,\"%s\"", network_id); - if (mm_serial_send_command_string (MM_SERIAL (modem), command)) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 30, responses, responses, - register_manual_done, info); - - g_free (command); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Manual registration failed."); - mm_callback_info_schedule (info); - } -} - -static gboolean -automatic_registration_again (gpointer data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) data; - - register_auto (MM_MODEM_GSM_NETWORK (info->modem), info); - - return FALSE; -} -static void -register_auto_done (MMModem *modem, - guint result, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - if (error) { - info->error = g_error_copy (error); - goto out; - } - - switch (result) { - case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "%s", "Automatic registration failed: not registered and not searching."); - break; - case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME: - g_message ("Registered on Home network"); - break; - case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING: - MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_id = g_timeout_add (1000, automatic_registration_again, info); - return; - break; - case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", - "Automatic registration failed: registration denied"); - break; - case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING: - g_message ("Registered on Roaming network"); - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration timed out"); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Automatic registration failed"); - break; - } - - out: - - if (info->error) - mm_callback_info_schedule (info); + if (network_id) + command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id); else - read_operator (MM_GENERIC_GSM (modem), info); -} + command = g_strdup ("+COPS=0,,"); -static void -register_auto (MMModemGsmNetwork *modem, MMCallbackInfo *info) -{ - get_registration_status (modem, register_auto_done, info); + mm_serial_queue_command (serial, command, 60, register_done, info); + g_free (command); } static void @@ -658,14 +502,11 @@ do_register (MMModemGsmNetwork *modem, MMCallbackInfo *info; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_set_data (info, "reg-network-id", g_strdup (network_id), g_free); - if (network_id) - register_manual (modem, network_id, info); - else - register_auto (modem, info); + real_register (MM_SERIAL (modem), network_id, info); } - static void get_registration_info_done (MMModem *modem, GError *error, gpointer user_data) { @@ -698,37 +539,39 @@ get_registration_info (MMModemGsmNetwork *self, } static void -connect_done (MMSerial *serial, - int reply_index, - gpointer user_data) +connect_report_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ - break; - case 1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: Busy"); - break; - case 2: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No dial tone"); - break; - case 3: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed: No carrier"); - break; - case -1: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing timed out"); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed"); - break; + if (!error && g_str_has_prefix (response->str, "+CEER: ")) { + g_free (info->error->message); + info->error->message = g_strdup (response->str + 7); /* skip the "+CEER: " */ } - + mm_callback_info_schedule (info); } static void +connect_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + info->error = g_error_copy (error); + /* Try to get more information why it failed */ + mm_serial_queue_command (serial, "+CEER", 3, connect_report_done, info); + } else + /* Done */ + mm_callback_info_schedule (info); +} + +static void connect (MMModem *modem, const char *number, MMModemFn callback, @@ -736,8 +579,6 @@ connect (MMModem *modem, { MMCallbackInfo *info; char *command; - char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL }; - guint id = 0; guint32 cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)); info = mm_callback_info_new (modem, callback, user_data); @@ -745,7 +586,7 @@ connect (MMModem *modem, if (cid > 0) { GString *str; - str = g_string_new ("ATD"); + str = g_string_new ("D"); if (g_str_has_suffix (number, "#")) str = g_string_append_len (str, number, strlen (number) - 1); else @@ -754,17 +595,16 @@ connect (MMModem *modem, g_string_append_printf (str, "***%d#", cid); command = g_string_free (str, FALSE); } else - command = g_strconcat ("ATDT", number, NULL); - - if (mm_serial_send_command_string (MM_SERIAL (modem), command)) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 60, responses, responses, connect_done, info); + command = g_strconcat ("DT", number, NULL); + mm_serial_queue_command (MM_SERIAL (modem), command, 60, connect_done, info); g_free (command); +} - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dialing failed."); - mm_callback_info_schedule (info); - } +static void +disconnect_flash_done (MMSerial *serial, gpointer user_data) +{ + mm_callback_info_schedule ((MMCallbackInfo *) user_data); } static void @@ -775,8 +615,7 @@ disconnect (MMModem *modem, MMCallbackInfo *info; info = mm_callback_info_new (modem, callback, user_data); - mm_serial_close (MM_SERIAL (modem)); - mm_callback_info_schedule (info); + mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info); } static void @@ -806,15 +645,19 @@ destroy_scan_data (gpointer data) } static void -scan_done (MMSerial *serial, const char *reply, gpointer user_data) +scan_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - GPtrArray *results; + char *reply = response->str; - results = g_ptr_array_new (); - - if (reply && !strncmp (reply, "+COPS: ", 7)) { + if (error) + info->error = g_error_copy (error); + else if (!strncmp (reply, "+COPS: ", 7)) { /* Got valid reply */ + GPtrArray *results; GRegex *r; GMatchInfo *match_info; GError *err = NULL; @@ -830,6 +673,8 @@ scan_done (MMSerial *serial, const char *reply, gpointer user_data) goto out; } + results = g_ptr_array_new (); + g_regex_match (r, reply, 0, &match_info); while (g_match_info_matches (match_info)) { GHashTable *hash; @@ -844,12 +689,10 @@ scan_done (MMSerial *serial, const char *reply, gpointer user_data) g_match_info_next (match_info, NULL); } + mm_callback_info_set_data (info, "scan-results", results, destroy_scan_data); g_match_info_free (match_info); g_regex_unref (r); - } else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse scan results"); - - mm_callback_info_set_data (info, "scan-results", results, destroy_scan_data); + } out: mm_callback_info_schedule (info); @@ -861,39 +704,27 @@ scan (MMModemGsmNetwork *modem, gpointer user_data) { MMCallbackInfo *info; - char *terminators = "\r\n"; - guint id = 0; info = mm_callback_info_new (MM_MODEM (modem), scan_callback_wrapper, NULL); info->user_data = info; mm_callback_info_set_data (info, "scan-callback", callback, NULL); mm_callback_info_set_data (info, "scan-data", user_data, NULL); - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+COPS=?")) - id = mm_serial_get_reply (MM_SERIAL (modem), 60, terminators, scan_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Scanning failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (MM_SERIAL (modem), "+COPS=?", 60, scan_done, info); } static void set_apn_done (MMSerial *serial, - int reply_index, + GString *response, + GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - switch (reply_index) { - case 0: - /* success */ + if (error) + info->error = g_error_copy (error); + else MM_GENERIC_GSM_GET_PRIVATE (serial)->cid = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "CID")); - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting APN failed"); - break; - } mm_callback_info_schedule (info); } @@ -906,32 +737,28 @@ set_apn (MMModemGsmNetwork *modem, { MMCallbackInfo *info; char *command; - char *responses[] = { "OK", "ERROR", NULL }; guint cid = 1; - guint id = 0; info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); mm_callback_info_set_data (info, "CID", GUINT_TO_POINTER (cid), NULL); - command = g_strdup_printf ("AT+CGDCONT=%d, \"IP\", \"%s\"", cid, apn); - if (mm_serial_send_command_string (MM_SERIAL (modem), command)) - id = mm_serial_wait_for_reply (MM_SERIAL (modem), 3, responses, responses, set_apn_done, info); - + command = g_strdup_printf ("+CGDCONT=%d, \"IP\", \"%s\"", cid, apn); + mm_serial_queue_command (MM_SERIAL (modem), command, 3, set_apn_done, info); g_free (command); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Setting APN failed."); - mm_callback_info_schedule (info); - } } static void -get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data) +get_signal_quality_done (MMSerial *serial, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - guint32 result = 0; + char *reply = response->str; - if (reply && !strncmp (reply, "+CSQ: ", 6)) { + if (error) + info->error = g_error_copy (error); + else if (!strncmp (reply, "+CSQ: ", 6)) { /* Got valid reply */ int quality; int ber; @@ -942,15 +769,14 @@ get_signal_quality_done (MMSerial *serial, const char *reply, gpointer user_data /* 99 means unknown */ if (quality != 99) /* Normalize the quality */ - result = quality * 100 / 31; + quality = quality * 100 / 31; + + mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); } else info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Could not parse signal quality results"); - } else - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "%s", "Could not parse signal quality results"); + } - mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL); mm_callback_info_schedule (info); } @@ -960,18 +786,9 @@ get_signal_quality (MMModemGsmNetwork *modem, gpointer user_data) { MMCallbackInfo *info; - char *terminators = "\r\n"; - guint id = 0; info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - - if (mm_serial_send_command_string (MM_SERIAL (modem), "AT+CSQ")) - id = mm_serial_get_reply (MM_SERIAL (modem), 10, terminators, get_signal_quality_done, info); - - if (!id) { - info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Getting signal quality failed."); - mm_callback_info_schedule (info); - } + mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info); } /*****************************************************************************/ @@ -1006,6 +823,10 @@ modem_gsm_network_init (MMModemGsmNetwork *class) static void mm_generic_gsm_init (MMGenericGsm *self) { + mm_serial_set_response_parser (MM_SERIAL (self), + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); } static void diff --git a/src/mm-manager.c b/src/mm-manager.c index 4f18633e..c139046e 100644 --- a/src/mm-manager.c +++ b/src/mm-manager.c @@ -6,7 +6,7 @@ #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include "mm-manager.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-generic-gsm.h" #include "mm-generic-cdma.h" #include "mm-plugin.h" @@ -454,5 +454,11 @@ mm_manager_class_init (MMManagerClass *manager_class) dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class), &dbus_glib_mm_manager_object_info); + /* FIXME: Sigh, these don't work either */ +#if 0 + dbus_g_error_domain_register (MM_SERIAL_ERROR, NULL, MM_TYPE_SERIAL_ERROR); dbus_g_error_domain_register (MM_MODEM_ERROR, NULL, MM_TYPE_MODEM_ERROR); + dbus_g_error_domain_register (MM_MODEM_CONNECT_ERROR, NULL, MM_TYPE_MODEM_CONNECT_ERROR); + dbus_g_error_domain_register (MM_MOBILE_ERROR, NULL, MM_TYPE_MOBILE_ERROR); +#endif } diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c index 7b0321e5..f58122a8 100644 --- a/src/mm-modem-cdma.c +++ b/src/mm-modem-cdma.c @@ -3,7 +3,7 @@ #include <string.h> #include <dbus/dbus-glib.h> #include "mm-modem-cdma.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-callback-info.h" static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context); diff --git a/src/mm-modem-error.c b/src/mm-modem-error.c deleted file mode 100644 index c415fb1f..00000000 --- a/src/mm-modem-error.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -#include "mm-modem-error.h" - -GQuark -mm_modem_error_quark (void) -{ - static GQuark ret = 0; - - if (ret == 0) - ret = g_quark_from_static_string ("mm_modem_error"); - - return ret; -} - -#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } - -GType -mm_modem_error_get_type (void) -{ - static GType etype = 0; - - if (etype == 0) { - static const GEnumValue values[] = { - ENUM_ENTRY (MM_MODEM_ERROR_GENERAL, "GeneralError"), - ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, "OperationNotSupported"), - ENUM_ENTRY (MM_MODEM_ERROR_PIN_NEEDED, "PINNeeded"), - ENUM_ENTRY (MM_MODEM_ERROR_PUK_NEEDED, "PUKNeeded"), - ENUM_ENTRY (MM_MODEM_ERROR_INVALID_SECRET, "InvalidSecret"), - { 0, 0, 0 } - }; - - etype = g_enum_register_static ("MMModemError", values); - } - - return etype; -} diff --git a/src/mm-modem-error.h b/src/mm-modem-error.h deleted file mode 100644 index f5c3c57e..00000000 --- a/src/mm-modem-error.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -#ifndef MM_MODEM_ERROR_H -#define MM_MODEM_ERROR_H - -#include <glib-object.h> - -enum { - MM_MODEM_ERROR_GENERAL = 0, - MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, - MM_MODEM_ERROR_PIN_NEEDED, - MM_MODEM_ERROR_PUK_NEEDED, - MM_MODEM_ERROR_INVALID_SECRET -}; - -#define MM_MODEM_ERROR (mm_modem_error_quark ()) -#define MM_TYPE_MODEM_ERROR (mm_modem_error_get_type ()) - -GQuark mm_modem_error_quark (void); -GType mm_modem_error_get_type (void); - -#endif /* MM_MODEM_ERROR_H */ diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c index 3a5502a6..1095d968 100644 --- a/src/mm-modem-gsm-card.c +++ b/src/mm-modem-gsm-card.c @@ -3,7 +3,7 @@ #include <dbus/dbus-glib.h> #include "mm-modem-gsm-card.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-callback-info.h" static void impl_gsm_modem_get_imei (MMModemGsmCard *modem, diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c index e83b48c6..7e170803 100644 --- a/src/mm-modem-gsm-network.c +++ b/src/mm-modem-gsm-network.c @@ -4,7 +4,7 @@ #include <dbus/dbus-glib.h> #include "mm-modem-gsm-network.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-callback-info.h" static void impl_gsm_modem_register (MMModemGsmNetwork *modem, diff --git a/src/mm-modem.c b/src/mm-modem.c index 64fc6429..a1fbd1c3 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -3,7 +3,7 @@ #include <string.h> #include <dbus/dbus-glib.h> #include "mm-modem.h" -#include "mm-modem-error.h" +#include "mm-errors.h" #include "mm-callback-info.h" static void impl_modem_enable (MMModem *modem, gboolean enable, DBusGMethodInvocation *context); diff --git a/src/mm-options.c b/src/mm-options.c new file mode 100644 index 00000000..190c99ea --- /dev/null +++ b/src/mm-options.c @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include <stdlib.h> +#include <glib.h> +#include "mm-options.h" + +static gboolean debug = FALSE; + +void +mm_options_parse (int argc, char *argv[]) +{ + GOptionContext *opt_ctx; + GError *error = NULL; + GOptionEntry entries[] = { + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, + { NULL } + }; + + opt_ctx = g_option_context_new (NULL); + g_option_context_set_summary (opt_ctx, "DBus system service to communicate with modems."); + g_option_context_add_main_entries (opt_ctx, entries, NULL); + + if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { + g_warning ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (opt_ctx); +} + +gboolean +mm_options_debug (void) +{ + return debug; +} diff --git a/src/mm-options.h b/src/mm-options.h new file mode 100644 index 00000000..d9190bc3 --- /dev/null +++ b/src/mm-options.h @@ -0,0 +1,9 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef MM_OPTIONS_H +#define MM_OPTIONS_H + +void mm_options_parse (int argc, char *argv[]); +gboolean mm_options_debug (void); + +#endif /* MM_OPTIONS_H */ diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c new file mode 100644 index 00000000..cc23cae8 --- /dev/null +++ b/src/mm-serial-parsers.c @@ -0,0 +1,266 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include <string.h> +#include <stdlib.h> + +#include "mm-serial-parsers.h" +#include "mm-errors.h" + +/* Clean up the response by removing control characters like <CR><LF> etc */ +static void +response_clean (GString *response) +{ + char *s; + + /* Ends with '<CR><LF>' */ + s = response->str + response->len - 1; + if (*s == '\n' && *(--s) == '\r') + g_string_truncate (response, response->len - 2); + + /* Starts with '<CR><LF>' */ + s = response->str; + if (*s == '\r' && *(++s) == '\n') + g_string_erase (response, 0, 2); +} + + +static gboolean +remove_eval_cb (const GMatchInfo *match_info, + GString *result, + gpointer user_data) +{ + int *result_len = (int *) user_data; + int start; + int end; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end)) + *result_len -= (end - start); + + return TRUE; +} + +static void +remove_matches (GRegex *r, GString *string) +{ + char *str; + int result_len = string->len; + + str = g_regex_replace_eval (r, string->str, string->len, 0, 0, + remove_eval_cb, &result_len, NULL); + + g_string_truncate (string, 0); + g_string_append_len (string, str, result_len); + g_free (str); +} + +/* FIXME: V0 parser is not finished */ +#if 0 +typedef struct { + GRegex *generic_response; + GRegex *detailed_error; +} MMSerialParserV0; + +gpointer +mm_serial_parser_v0_new (void) +{ + MMSerialParserV0 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV0); + + parser->generic_response = g_regex_new ("(\\d)\\r%", flags, 0, NULL); + parser->detailed_error = g_regex_new ("+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v0_parse (gpointer parser, + GString *response, + GError **error) +{ + MMSerialParserV0 *parser = (MMSerialParserV0 *) data; + GMatchInfo *match_info; + char *str; + int code; + gboolean found; + + found = g_regex_match_full (parser->generic_response, response->str, response->len, 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } + + g_match_info_free (match_info); + + return TRUE; + } + + found = g_regex_match_full (parser->detailed_error, response->str, response->len, 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } else + code = MM_MOBILE_ERROR_UNKNOWN; + + g_match_info_free (match_info); + + g_debug ("Got error code %d: %s", code, msg); + g_set_error (error, MM_MOBILE_ERROR, code, "%s", msg); + + return TRUE; + } + + return FALSE; +} + +void +mm_serial_parser_v0_destroy (gpointer parser) +{ + MMSerialParserV0 *parser = (MMSerialParserV0 *) data; + + g_regex_unref (parser->generic_response); + g_regex_unref (parser->detailed_error); + + g_slice_free (MMSerialParserV0, data); +} +#endif + +typedef struct { + GRegex *regex_ok; + GRegex *regex_connect; + GRegex *regex_detailed_error; + GRegex *regex_unknown_error; + GRegex *regex_connect_failed; +} MMSerialParserV1; + +gpointer +mm_serial_parser_v1_new (void) +{ + MMSerialParserV1 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV1); + + parser->regex_ok = g_regex_new ("\\r\\nOK\\r\\n$", flags, 0, NULL); + parser->regex_connect = g_regex_new ("\\r\\nCONNECT\\s*\\d*\\r\\n$", flags, 0, NULL); + parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL); + parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v1_parse (gpointer data, + GString *response, + GError **error) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + GMatchInfo *match_info; + const char *msg; + int code; + gboolean found = FALSE; + + /* First, check for successfule responses */ + + found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); + if (found) + remove_matches (parser->regex_ok, response); + else + found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL); + + if (found) { + response_clean (response); + return TRUE; + } + + /* Now failures */ + code = MM_MOBILE_ERROR_UNKNOWN; + + found = g_regex_match_full (parser->regex_detailed_error, + response->str, response->len, + 0, 0, &match_info, NULL); + + if (found) { + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } + g_match_info_free (match_info); + } else + found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, NULL, NULL); + + if (found) { +#if 0 + /* FIXME: This does not work for some reason. */ + GEnumValue *enum_value; + + enum_value = g_enum_get_value ((GEnumClass *) g_type_class_peek_static (), code); + msg = enum_value->value_nick; +#endif + msg = "FIXME"; + + g_debug ("Got error code %d: %s", code, msg); + g_set_error (error, MM_MOBILE_ERROR, code, "%s", msg); + } else { + found = g_regex_match_full (parser->regex_connect_failed, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str) { + if (!strcmp (str, "NO CARRIER")) { + code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + msg = "No carrier"; + } else if (!strcmp (str, "BUSY")) { + code = MM_MODEM_CONNECT_ERROR_BUSY; + msg = "Busy"; + } else if (!strcmp (str, "NO ANSWER")) { + code = MM_MODEM_CONNECT_ERROR_NO_ANSWER; + msg = "No answer"; + } else if (!strcmp (str, "NO DIALTONE")) { + code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE; + msg = "No dialtone"; + } else { + g_warning ("Got matching connect failure, but can't parse it"); + /* uhm... make something up (yes, ok, lie!). */ + code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + msg = "No carrier"; + } + + g_free (str); + } + g_match_info_free (match_info); + + g_debug ("Got connect failure code %d: %s", code, msg); + g_set_error (error, MM_MODEM_CONNECT_ERROR, code, "%s", msg); + } + } + + return found; + +} + +void +mm_serial_parser_v1_destroy (gpointer data) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + + g_regex_unref (parser->regex_ok); + g_regex_unref (parser->regex_connect); + g_regex_unref (parser->regex_detailed_error); + g_regex_unref (parser->regex_unknown_error); + g_regex_unref (parser->regex_connect_failed); + + g_slice_free (MMSerialParserV1, data); +} diff --git a/src/mm-serial-parsers.h b/src/mm-serial-parsers.h new file mode 100644 index 00000000..8ef6ef03 --- /dev/null +++ b/src/mm-serial-parsers.h @@ -0,0 +1,26 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef MM_SERIAL_PARSERS_H +#define MM_SERIAL_PARSERS_H + +#include <glib.h> + +/* FIXME: V0 parser is not finished */ +#if 0 +gpointer mm_serial_parser_v0_new (void); +gboolean mm_serial_parser_v0_parse (gpointer parser, + GString *response, + GError **error); + +void mm_serial_parser_v0_destroy (gpointer parser); +#endif + + +gpointer mm_serial_parser_v1_new (void); +gboolean mm_serial_parser_v1_parse (gpointer parser, + GString *response, + GError **error); + +void mm_serial_parser_v1_destroy (gpointer parser); + +#endif /* MM_SERIAL_PARSERS_H */ diff --git a/src/mm-serial.c b/src/mm-serial.c index 284db4d2..f941feff 100644 --- a/src/mm-serial.c +++ b/src/mm-serial.c @@ -2,6 +2,8 @@ #define _GNU_SOURCE /* for strcasestr() */ +#include <stdio.h> +#include <stdlib.h> #include <termio.h> #include <unistd.h> #include <sys/types.h> @@ -12,6 +14,10 @@ #include <string.h> #include "mm-serial.h" +#include "mm-errors.h" +#include "mm-options.h" + +static gboolean mm_serial_queue_process (gpointer data); G_DEFINE_TYPE (MMSerial, mm_serial, G_TYPE_OBJECT) @@ -27,7 +33,6 @@ enum { LAST_PROP }; -#define MM_DEBUG_SERIAL 1 #define SERIAL_BUF_SIZE 2048 #define MM_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL, MMSerialPrivate)) @@ -35,6 +40,15 @@ enum { typedef struct { int fd; GIOChannel *channel; + GQueue *queue; + GString *command; + GString *response; + + /* Response parser data */ + MMSerialResponseParserFn response_parser_fn; + gpointer response_parser_user_data; + GDestroyNotify response_parser_notify; + struct termios old_t; char *device; @@ -44,7 +58,8 @@ typedef struct { guint stopbits; guint64 send_delay; - guint pending_id; + guint watch_id; + guint timeout_id; } MMSerialPrivate; const char * @@ -189,86 +204,6 @@ parse_stopbits (guint i) return stopbits; } -#ifdef MM_DEBUG_SERIAL -static inline void -serial_debug (const char *prefix, const char *data, int len) -{ - GString *str; - int i; - - str = g_string_sized_new (len); - for (i = 0; i < len; i++) { - if (data[i] == '\0') - g_string_append_c (str, ' '); - else if (data[i] == '\r') - g_string_append_c (str, '\n'); - else - g_string_append_c (str, data[i]); - } - - g_debug ("%s '%s'", prefix, str->str); - g_string_free (str, TRUE); -} -#else -static inline void -serial_debug (const char *prefix, const char *data, int len) -{ -} -#endif /* MM_DEBUG_SERIAL */ - -/* Pending data reading */ - -static gboolean -mm_serial_timed_out (gpointer data) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (data); - - /* Cancel data reading */ - if (priv->pending_id) - g_source_remove (priv->pending_id); - - return FALSE; -} - -static guint -mm_serial_set_pending (MMSerial *self, - guint timeout, - GIOFunc callback, - gpointer user_data, - GDestroyNotify notify) -{ - MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - GSource *source; - - if (G_UNLIKELY (priv->pending_id)) { - /* FIXME: Probably should queue up pending calls instead? */ - /* Multiple pending calls on the same GIOChannel doesn't work, so let's cancel the previous one. */ - g_warning ("Adding new pending call while previous one isn't finished."); - g_warning ("Cancelling the previous pending call."); - g_source_remove (priv->pending_id); - } - - priv->pending_id = g_io_add_watch_full (priv->channel, - G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_ERR | G_IO_HUP, - callback, user_data, notify); - - source = g_timeout_source_new (timeout * 100); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_timed_out), G_OBJECT (self))); - g_source_attach (source, NULL); - g_source_unref (source); - - return priv->pending_id; -} - -static void -mm_serial_pending_done (MMSerial *self) -{ - MM_SERIAL_GET_PRIVATE (self)->pending_id = 0; -} - -/****/ - static gboolean config_fd (MMSerial *self) { @@ -305,312 +240,203 @@ config_fd (MMSerial *self) return TRUE; } -gboolean -mm_serial_open (MMSerial *self) +static void +serial_debug (const char *prefix, const char *buf, int len) { - MMSerialPrivate *priv; - - g_return_val_if_fail (MM_IS_SERIAL (self), FALSE); - - priv = MM_SERIAL_GET_PRIVATE (self); + const char *s; - if (priv->fd) - /* Already open */ - return TRUE; + if (!mm_options_debug ()) + return; - g_debug ("(%s) opening serial device...", priv->device); - priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); + if (len < 0) + len = strlen (buf); - if (priv->fd < 0) { - g_warning ("(%s) cannot open device: %s", priv->device, strerror (errno)); - return FALSE; - } + g_print ("%s '", prefix); - if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { - g_warning ("(%s) cannot control device (errno %d)", priv->device, errno); - close (priv->fd); - return FALSE; - } + s = buf; + while (len--) { + if (g_ascii_isprint (*s)) + g_print ("%c", *s); + else if (*s == '\r') + g_print ("<CR>"); + else if (*s == '\n') + g_print ("<LF>"); + else + g_print ("\\%d", *s); - if (!config_fd (self)) { - close (priv->fd); - priv->fd = 0; - return FALSE; + s++; } - priv->channel = g_io_channel_unix_new (priv->fd); - - return TRUE; -} - -void -mm_serial_close (MMSerial *self) -{ - MMSerialPrivate *priv; - - g_return_if_fail (MM_IS_SERIAL (self)); - - priv = MM_SERIAL_GET_PRIVATE (self); - - if (priv->pending_id) - g_source_remove (priv->pending_id); - - if (priv->fd) { - g_message ("Closing device '%s'", priv->device); - - if (priv->channel) { - g_io_channel_unref (priv->channel); - priv->channel = NULL; - } - - ioctl (priv->fd, TCSETA, &priv->old_t); - close (priv->fd); - priv->fd = 0; - } + g_print ("'\n"); } -gboolean -mm_serial_send_command (MMSerial *self, GByteArray *command) +static gboolean +mm_serial_send_command (MMSerial *self, const char *command) { - MMSerialPrivate *priv; - int fd; - int i; - ssize_t status; - - g_return_val_if_fail (MM_IS_SERIAL (self), FALSE); - g_return_val_if_fail (command != NULL, FALSE); + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + const char *s; + int status; - priv = MM_SERIAL_GET_PRIVATE (self); + g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2); + g_string_append (priv->command, command); - fd = priv->fd; + if (command[strlen (command)] != '\r') + g_string_append_c (priv->command, '\r'); - serial_debug ("Sending:", (char *) command->data, command->len); + serial_debug ("-->", priv->command->str, -1); - for (i = 0; i < command->len; i++) { + s = priv->command->str; + while (*s) { again: - status = write (fd, command->data + i, 1); - + status = write (priv->fd, s, 1); if (status < 0) { if (errno == EAGAIN) goto again; - - g_warning ("Error in writing (errno %d)", errno); - return FALSE; + else { + g_warning ("Error writing to serial device: %s", strerror (errno)); + break; + } } if (priv->send_delay) usleep (priv->send_delay); - } - return TRUE; -} - -gboolean -mm_serial_send_command_string (MMSerial *self, const char *str) -{ - GByteArray *command; - gboolean ret; - - g_return_val_if_fail (MM_IS_SERIAL (self), FALSE); - g_return_val_if_fail (str != NULL, FALSE); - - command = g_byte_array_new (); - g_byte_array_append (command, (guint8 *) str, strlen (str)); - g_byte_array_append (command, (guint8 *) "\r", 1); - - ret = mm_serial_send_command (self, command); - g_byte_array_free (command, TRUE); + s++; + } - return ret; + return *s == '\0'; } typedef struct { - MMSerial *serial; - char *terminators; - GString *result; - MMSerialGetReplyFn callback; + char *command; + MMSerialResponseFn callback; gpointer user_data; -} GetReplyInfo; + guint32 timeout; +} MMQueueData; static void -get_reply_done (gpointer data) +mm_serial_got_response (MMSerial *self, GError *error) { - GetReplyInfo *info = (GetReplyInfo *) data; + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + MMQueueData *info; - mm_serial_pending_done (info->serial); + if (priv->timeout_id) + g_source_remove (priv->timeout_id); - /* Call the callback */ - info->callback (info->serial, info->result->str, info->user_data); + info = (MMQueueData *) g_queue_pop_head (priv->queue); + if (info) { + if (info->callback) + info->callback (self, priv->response, error, info->user_data); - /* Free info */ - g_free (info->terminators); - g_string_free (info->result, TRUE); + g_free (info->command); + g_slice_free (MMQueueData, info); + } + + if (error) + g_error_free (error); - g_slice_free (GetReplyInfo, info); + g_string_truncate (priv->response, 0); + if (!g_queue_is_empty (priv->queue)) + g_idle_add (mm_serial_queue_process, self); } static gboolean -get_reply_got_data (GIOChannel *source, - GIOCondition condition, - gpointer data) +mm_serial_timed_out (gpointer data) { - GetReplyInfo *info = (GetReplyInfo *) data; - gsize bytes_read; - char buf[SERIAL_BUF_SIZE + 1]; - GIOStatus status; - gboolean done = FALSE; - int i; + MMSerial *self = MM_SERIAL (data); + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + GError *error; - if (condition & G_IO_HUP || condition & G_IO_ERR) { - g_string_truncate (info->result, 0); - return FALSE; - } + priv->timeout_id = 0; - do { - GError *err = NULL; + error = g_error_new (MM_SERIAL_ERROR, + MM_SERIAL_RESPONSE_TIMEOUT, + "%s", "Serial command timed out"); + /* FIXME: This is not completely correct - if the response finally arrives and there's + some other command waiting for response right now, the other command will + get the output of the timed out command. Maybe flashing would help here? */ + mm_serial_got_response (self, error); - status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err); - if (status == G_IO_STATUS_ERROR) { - g_warning ("%s", err->message); - g_error_free (err); - err = NULL; - } + return FALSE; +} - if (bytes_read > 0) { - char *p; - - serial_debug ("Got:", buf, bytes_read); - - p = &buf[0]; - for (i = 0; i < bytes_read && !done; i++, p++) { - int j; - gboolean is_terminator = FALSE; - - for (j = 0; j < strlen (info->terminators); j++) { - if (*p == info->terminators[j]) { - is_terminator = TRUE; - break; - } - } - - if (is_terminator) { - /* Ignore terminators in the beginning of the output */ - if (info->result->len > 0) - done = TRUE; - } else - g_string_append_c (info->result, *p); - } - } +static gboolean +mm_serial_queue_process (gpointer data) +{ + MMSerial *self = MM_SERIAL (data); + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + MMQueueData *info; - /* Limit the size of the buffer */ - if (info->result->len > SERIAL_BUF_SIZE) { - g_warning ("%s (%s): response buffer filled before repsonse received", - __func__, MM_SERIAL_GET_PRIVATE (info->serial)->device); - g_string_truncate (info->result, 0); - done = TRUE; - } - } while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); + info = (MMQueueData *) g_queue_peek_head (priv->queue); + if (!info) + return FALSE; - return !done; -} + if (mm_serial_send_command (self, info->command)) { + GSource *source; -guint -mm_serial_get_reply (MMSerial *self, - guint timeout, - const char *terminators, - MMSerialGetReplyFn callback, - gpointer user_data) -{ - GetReplyInfo *info; + source = g_timeout_source_new (info->timeout); + g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_timed_out), G_OBJECT (self))); + g_source_attach (source, NULL); + priv->timeout_id = g_source_get_id (source); + g_source_unref (source); + } else { + GError *error; - g_return_val_if_fail (MM_IS_SERIAL (self), 0); - g_return_val_if_fail (terminators != NULL, 0); - g_return_val_if_fail (callback != NULL, 0); + error = g_error_new (MM_SERIAL_ERROR, + MM_SERIAL_SEND_FAILED, + "%s", "Sending command failed"); - info = g_slice_new0 (GetReplyInfo); - info->serial = self; - info->terminators = g_strdup (terminators); - info->result = g_string_new (NULL); - info->callback = callback; - info->user_data = user_data; + mm_serial_got_response (self, error); + } - return mm_serial_set_pending (self, timeout, get_reply_got_data, info, get_reply_done); + return FALSE; } -typedef struct { - MMSerial *serial; - char **str_needles; - char **terminators; - GString *result; - MMSerialWaitForReplyFn callback; - gpointer user_data; - int reply_index; - guint timeout; - time_t start; -} WaitForReplyInfo; - -static void -wait_for_reply_done (gpointer data) +void +mm_serial_set_response_parser (MMSerial *self, + MMSerialResponseParserFn fn, + gpointer user_data, + GDestroyNotify notify) { - WaitForReplyInfo *info = (WaitForReplyInfo *) data; - - mm_serial_pending_done (info->serial); + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - /* Call the callback */ - info->callback (info->serial, info->reply_index, info->user_data); + g_return_if_fail (MM_IS_SERIAL (self)); - /* Free info */ - if (info->result) - g_string_free (info->result, TRUE); + if (priv->response_parser_notify) + priv->response_parser_notify (priv->response_parser_user_data); - g_strfreev (info->str_needles); - g_strfreev (info->terminators); - g_slice_free (WaitForReplyInfo, info); + priv->response_parser_fn = fn; + priv->response_parser_user_data = user_data; + priv->response_parser_notify = notify; } static gboolean -find_terminator (const char *line, char **terminators) +parse_response (MMSerial *self, + GString *response, + GError **error) { - int i; + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); - for (i = 0; terminators[i]; i++) { - if (!strncasecmp (line, terminators[i], strlen (terminators[i]))) - return TRUE; - } - return FALSE; -} + g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE); -static gboolean -find_response (const char *line, char **responses, gint *idx) -{ - int i; - - /* Don't look for a result again if we got one previously */ - for (i = 0; responses[i]; i++) { - if (strcasestr (line, responses[i])) { - *idx = i; - return TRUE; - } - } - return FALSE; + return priv->response_parser_fn (priv->response_parser_user_data, response, error); } -#define RESPONSE_LINE_MAX 128 - static gboolean -wait_for_reply_got_data (GIOChannel *source, - GIOCondition condition, - gpointer data) +data_available (GIOChannel *source, + GIOCondition condition, + gpointer data) { - WaitForReplyInfo *info = (WaitForReplyInfo *) data; - gchar buf[SERIAL_BUF_SIZE + 1]; + MMSerial *self = MM_SERIAL (data); + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + char buf[SERIAL_BUF_SIZE + 1]; gsize bytes_read; GIOStatus status; - gboolean got_response = FALSE; - gboolean done = FALSE; - if (condition & G_IO_HUP || condition & G_IO_ERR) - return FALSE; + if (condition & G_IO_HUP || condition & G_IO_ERR) { + g_string_truncate (priv->response, 0); + return TRUE; + } do { GError *err = NULL; @@ -623,185 +449,112 @@ wait_for_reply_got_data (GIOChannel *source, } if (bytes_read > 0) { - buf[bytes_read] = 0; - g_string_append (info->result, buf); - - serial_debug ("Got:", info->result->str, info->result->len); + serial_debug ("<--", buf, bytes_read); + g_string_append_len (priv->response, buf, bytes_read); } - /* Look for needles and terminators */ - if ((bytes_read > 0) && info->result->str) { - char *p = info->result->str; - - /* Break the response up into lines and process each one */ - while ( (p < info->result->str + strlen (info->result->str)) - && !(done && got_response)) { - char line[RESPONSE_LINE_MAX] = { '\0', }; - char *tmp; - int i; - gboolean got_something = FALSE; - - for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) { - /* Ignore front CR/LF */ - if ((*p == '\n') || (*p == '\r')) { - if (got_something) - break; - } else { - line[i++] = *p; - got_something = TRUE; - } - } - line[i] = '\0'; - - tmp = g_strstrip (line); - if (tmp && strlen (tmp)) { - done = find_terminator (tmp, info->terminators); - if (info->reply_index == -1) - got_response = find_response (tmp, info->str_needles, &(info->reply_index)); - } - } - - if (done && got_response) - break; - } + if (parse_response (self, priv->response, &err)) + mm_serial_got_response (self, err); - /* Limit the size of the buffer */ - if (info->result->len > SERIAL_BUF_SIZE) { - g_warning ("%s (%s): response buffer filled before repsonse received", - __func__, MM_SERIAL_GET_PRIVATE (info->serial)->device); - done = TRUE; - break; - } + } while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); - /* Make sure we don't go over the timeout, in addition to the timeout - * handler that's been scheduled. If for some reason this loop doesn't - * terminate (terminator not found, whatever) then this should make - * sure that we don't spin the CPU forever. - */ - if (time (NULL) - info->start > info->timeout + 1) { - done = TRUE; - break; - } else - g_usleep (50); - } while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN); - - return !done; + return TRUE; } -guint -mm_serial_wait_for_reply (MMSerial *self, - guint timeout, - char **responses, - char **terminators, - MMSerialWaitForReplyFn callback, - gpointer user_data) +gboolean +mm_serial_open (MMSerial *self, GError **error) { - WaitForReplyInfo *info; + MMSerialPrivate *priv; - g_return_val_if_fail (MM_IS_SERIAL (self), 0); - g_return_val_if_fail (responses != NULL, 0); - g_return_val_if_fail (callback != NULL, 0); + g_return_val_if_fail (MM_IS_SERIAL (self), FALSE); - info = g_slice_new0 (WaitForReplyInfo); - info->serial = self; - info->str_needles = g_strdupv (responses); - info->terminators = g_strdupv (terminators); - info->result = g_string_new (NULL); - info->callback = callback; - info->user_data = user_data; - info->reply_index = -1; - info->timeout = timeout; - info->start = time (NULL); + priv = MM_SERIAL_GET_PRIVATE (self); - return mm_serial_set_pending (self, timeout, wait_for_reply_got_data, info, wait_for_reply_done); -} + if (priv->fd) + /* Already open */ + return TRUE; -#if 0 -typedef struct { - MMSerial *serial; - gboolean timed_out; - MMSerialWaitQuietFn callback; - gpointer user_data; -} WaitQuietInfo; + g_debug ("(%s) opening serial device...", priv->device); + priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); -static void -wait_quiet_done (gpointer data) -{ - WaitQuietInfo *info = (WaitQuietInfo *) data; + if (priv->fd < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", priv->device, strerror (errno)); + return FALSE; + } - mm_serial_pending_done (info->serial); + if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", priv->device, strerror (errno)); + close (priv->fd); + return FALSE; + } - /* Call the callback */ - info->callback (info->serial, info->timed_out, info->user_data); + if (!config_fd (self)) { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + "Could not open serial device %s: %s", priv->device, strerror (errno)); + close (priv->fd); + priv->fd = 0; + return FALSE; + } + + priv->channel = g_io_channel_unix_new (priv->fd); + priv->watch_id = g_io_add_watch (priv->channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + data_available, self); - /* Free info */ - g_slice_free (WaitQuietInfo, info); + return TRUE; } -static gboolean -wait_quiet_quiettime (gpointer data) +void +mm_serial_close (MMSerial *self) { - WaitQuietInfo *info = (WaitQuietInfo *) data; - - info->timed_out = FALSE; - g_source_remove (MM_SERIAL_GET_PRIVATE (info->serial)->pending); + MMSerialPrivate *priv; - return FALSE; -} + g_return_if_fail (MM_IS_SERIAL (self)); -static gboolean -wait_quiet_got_data (GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - WaitQuietInfo *info = (WaitQuietInfo *) data; - gsize bytes_read; - char buf[4096]; - GIOStatus status; + priv = MM_SERIAL_GET_PRIVATE (self); - if (condition & G_IO_HUP || condition & G_IO_ERR) - return FALSE; + if (priv->fd) { + g_message ("Closing device '%s'", priv->device); - if (condition & G_IO_IN) { - do { - status = g_io_channel_read_chars (source, buf, 4096, &bytes_read, NULL); + if (priv->channel) { + g_source_remove (priv->watch_id); + g_io_channel_shutdown (priv->channel, TRUE, NULL); + g_io_channel_unref (priv->channel); + priv->channel = NULL; + } - if (bytes_read) { - /* Reset the quiet time timeout */ - g_source_remove (info->quiet_id); - info->quiet_id = g_timeout_add (info->quiet_time, wait_quiet_quiettime, info); - } - } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN); + ioctl (priv->fd, TCSETA, &priv->old_t); + close (priv->fd); + priv->fd = 0; } - - return TRUE; } void -mm_serial_wait_quiet (MMSerial *self, - guint timeout, - guint quiet_time, - MMSerialWaitQuietFn callback, - gpointer user_data) +mm_serial_queue_command (MMSerial *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data) { - WaitQuietInfo *info; + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + MMQueueData *info; g_return_if_fail (MM_IS_SERIAL (self)); - g_return_if_fail (callback != NULL); + g_return_if_fail (command != NULL); - info = g_slice_new0 (WaitQuietInfo); - info->serial = self; - info->timed_out = TRUE; + info = g_slice_new0 (MMQueueData); + info->command = g_strdup (command); + info->timeout = timeout_seconds * 1000; info->callback = callback; info->user_data = user_data; - info->quiet_id = g_timeout_add (quiet_time, - wait_quiet_timeout, - info); - return mm_serial_set_pending (self, timeout, wait_quiet_got_data, info, wait_quiet_done); -} + g_queue_push_tail (priv->queue, info); -#endif + if (g_queue_get_length (priv->queue) == 1) + g_idle_add (mm_serial_queue_process, self); +} typedef struct { MMSerial *serial; @@ -841,8 +594,6 @@ flash_done (gpointer data) { FlashInfo *info = (FlashInfo *) data; - MM_SERIAL_GET_PRIVATE (info->serial)->pending_id = 0; - info->callback (info->serial, info->user_data); g_slice_free (FlashInfo, info); @@ -884,25 +635,9 @@ mm_serial_flash (MMSerial *self, info, flash_done); - MM_SERIAL_GET_PRIVATE (self)->pending_id = id; - return id; } -GIOChannel * -mm_serial_get_io_channel (MMSerial *self) -{ - MMSerialPrivate *priv; - - g_return_val_if_fail (MM_IS_SERIAL (self), NULL); - - priv = MM_SERIAL_GET_PRIVATE (self); - if (priv->channel) - return g_io_channel_ref (priv->channel); - - return NULL; -} - /*****************************************************************************/ static void @@ -915,6 +650,10 @@ mm_serial_init (MMSerial *self) priv->parity = 'n'; priv->stopbits = 1; priv->send_delay = 0; + + priv->queue = g_queue_new (); + priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE); + priv->response = g_string_sized_new (SERIAL_BUF_SIZE); } static GObject* @@ -1012,8 +751,15 @@ finalize (GObject *object) MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); mm_serial_close (self); + + g_queue_free (priv->queue); + g_string_free (priv->command, TRUE); + g_string_free (priv->response, TRUE); g_free (priv->device); + if (priv->response_parser_notify) + priv->response_parser_notify (priv->response_parser_user_data); + G_OBJECT_CLASS (mm_serial_parent_class)->finalize (object); } diff --git a/src/mm-serial.h b/src/mm-serial.h index bd138516..37162c6f 100644 --- a/src/mm-serial.h +++ b/src/mm-serial.h @@ -6,12 +6,12 @@ #include <glib/gtypes.h> #include <glib-object.h> -#define MM_TYPE_SERIAL (mm_serial_get_type ()) -#define MM_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL, MMSerial)) -#define MM_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL, MMSerialClass)) -#define MM_IS_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SERIAL)) -#define MM_IS_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL)) -#define MM_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL, MMSerialClass)) +#define MM_TYPE_SERIAL (mm_serial_get_type ()) +#define MM_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL, MMSerial)) +#define MM_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL, MMSerialClass)) +#define MM_IS_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SERIAL)) +#define MM_IS_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL)) +#define MM_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL, MMSerialClass)) #define MM_SERIAL_DEVICE "device" #define MM_SERIAL_BAUD "baud" @@ -20,66 +20,51 @@ #define MM_SERIAL_STOPBITS "stopbits" #define MM_SERIAL_SEND_DELAY "send-delay" -typedef struct { - GObject parent; -} MMSerial; - -typedef struct { - GObjectClass parent; -} MMSerialClass; - -GType mm_serial_get_type (void); - -typedef void (*MMSerialGetReplyFn) (MMSerial *serial, - const char *reply, - gpointer user_data); +typedef struct _MMSerial MMSerial; +typedef struct _MMSerialClass MMSerialClass; -typedef void (*MMSerialWaitForReplyFn) (MMSerial *serial, - int reply_index, - gpointer user_data); +typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data, + GString *response, + GError **error); -typedef void (*MMSerialWaitQuietFn) (MMSerial *serial, - gboolean timed_out, +typedef void (*MMSerialResponseFn) (MMSerial *serial, + GString *response, + GError *error, gpointer user_data); typedef void (*MMSerialFlashFn) (MMSerial *serial, gpointer user_data); -const char *mm_serial_get_device (MMSerial *serial); - -gboolean mm_serial_open (MMSerial *self); +struct _MMSerial { + GObject parent; +}; -void mm_serial_close (MMSerial *self); -gboolean mm_serial_send_command (MMSerial *self, - GByteArray *command); +struct _MMSerialClass { + GObjectClass parent; +}; -gboolean mm_serial_send_command_string (MMSerial *self, - const char *str); +GType mm_serial_get_type (void); -guint mm_serial_get_reply (MMSerial *self, - guint timeout, - const char *terminators, - MMSerialGetReplyFn callback, - gpointer user_data); +void mm_serial_set_response_parser (MMSerial *self, + MMSerialResponseParserFn fn, + gpointer user_data, + GDestroyNotify notify); -guint mm_serial_wait_for_reply (MMSerial *self, - guint timeout, - char **responses, - char **terminators, - MMSerialWaitForReplyFn callback, - gpointer user_data); +gboolean mm_serial_open (MMSerial *self, + GError **error); -void mm_serial_wait_quiet (MMSerial *self, - guint timeout, - guint quiet_time, - MMSerialWaitQuietFn callback, - gpointer user_data); +void mm_serial_close (MMSerial *self); +void mm_serial_queue_command (MMSerial *self, + const char *command, + guint32 timeout_seconds, + MMSerialResponseFn callback, + gpointer user_data); -guint mm_serial_flash (MMSerial *self, - guint32 flash_time, - MMSerialFlashFn callback, - gpointer user_data); +guint mm_serial_flash (MMSerial *self, + guint32 flash_time, + MMSerialFlashFn callback, + gpointer user_data); -GIOChannel *mm_serial_get_io_channel (MMSerial *self); +const char *mm_serial_get_device (MMSerial *self); #endif /* MM_SERIAL_H */ |