diff options
author | Tambet Ingo <tambet@gmail.com> | 2008-09-11 08:35:32 +0300 |
---|---|---|
committer | Tambet Ingo <tambet@gmail.com> | 2008-09-11 08:35:32 +0300 |
commit | ac4409e7cea29e03d311e6b805a084837d8bb70f (patch) | |
tree | 6b534ff91a3976a0268a89c858543bc973b17f8c /src | |
parent | bb874acea0c8552f86932084e222b45a94119f29 (diff) |
Rewrite serial device communications.
Instead of vague "send something, wait something" the responses are now
analyzed by (overridable) parsers. Makes all the modem implementations much
easier since each caller knows without any code whether the call succeeded
or failed.
Another thing that makes modem code simpler (and the whole thing more robust),
is the queueing of sent commands. Each queued command has a command and a
callback which is quaranteed to get called, even if sending failed.
Define and implement error reporting.
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 */ |