diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 273 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 3 | ||||
-rw-r--r-- | src/mm-serial.c | 97 | ||||
-rw-r--r-- | src/mm-serial.h | 11 | ||||
-rw-r--r-- | src/mm-util.c | 57 | ||||
-rw-r--r-- | src/mm-util.h | 20 |
7 files changed, 287 insertions, 178 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 4ba3b750..caafef8b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,9 +38,7 @@ modem_manager_SOURCES = \ mm-serial.c \ mm-serial.h \ mm-serial-parsers.c \ - mm-serial-parsers.h \ - mm-util.c \ - mm-util.h + mm-serial-parsers.h mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index a76cd698..b13623cd 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -22,11 +22,16 @@ typedef struct { char *oper_name; guint32 modem_type; guint32 ip_method; + gboolean unsolicited_registration; + MMModemGsmNetworkRegStatus reg_status; + guint pending_registration; + guint32 signal_quality; guint32 cid; } MMGenericGsmPrivate; +static void pending_registration_stop (MMGenericGsm *self); static void get_registration_status (MMSerial *serial, MMCallbackInfo *info); static void read_operator_done (MMSerial *serial, GString *response, @@ -47,6 +52,15 @@ mm_generic_gsm_new (const char *serial_device, const char *driver) } void +mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem, + gboolean enabled) +{ + g_return_if_fail (MM_IS_GENERIC_GSM (modem)); + + MM_GENERIC_GSM_GET_PRIVATE (modem)->unsolicited_registration = enabled; +} + +void mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid) { g_return_if_fail (MM_IS_GENERIC_GSM (modem)); @@ -62,6 +76,14 @@ mm_generic_gsm_get_cid (MMGenericGsm *modem) return MM_GENERIC_GSM_GET_PRIVATE (modem)->cid; } +static void +got_signal_quality (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ +} + void mm_generic_gsm_set_reg_status (MMGenericGsm *modem, MMModemGsmNetworkRegStatus status) @@ -75,11 +97,13 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem, if (priv->reg_status != status) { priv->reg_status = status; + g_debug ("Registration state changed: %d", status); + if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME || status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) { mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,2;+COPS?", 3, read_operator_done, GINT_TO_POINTER (0)); mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,0;+COPS?", 3, read_operator_done, GINT_TO_POINTER (1)); - mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), NULL, NULL); + mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL); } else { g_free (priv->oper_code); g_free (priv->oper_name); @@ -162,8 +186,11 @@ init_done (MMSerial *serial, info->error = g_error_copy (error); mm_callback_info_schedule (info); } else { - /* Disable unsolicited registration state changes, these will mess up our response parser */ - mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL); + if (MM_GENERIC_GSM_GET_PRIVATE (serial)->unsolicited_registration) + mm_serial_queue_command (serial, "+CREG=1", 5, NULL, NULL); + else + mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL); + mm_serial_queue_command (serial, "+CFUN=1", 5, enable_done, user_data); } } @@ -204,6 +231,8 @@ enable (MMModem *modem, info = mm_callback_info_new (modem, callback, user_data); if (!do_enable) { + pending_registration_stop (MM_GENERIC_GSM (modem)); + if (mm_serial_is_connected (MM_SERIAL (modem))) mm_serial_flash (MM_SERIAL (modem), 1000, disable_flash_done, info); else @@ -496,32 +525,124 @@ read_operator_done (MMSerial *serial, } } +/* Registration */ + +static gboolean +pending_registration_timed_out (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + + MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration = 0; + + return FALSE; +} + +static void +pending_registration_stop (MMGenericGsm *self) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + if (priv->pending_registration) { + g_source_remove (priv->pending_registration); + priv->pending_registration = 0; + } +} + +static void +pending_registration_done (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + + if (!info->error) { + switch (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->reg_status) { + case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME: + case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING: + /* Successfully registered */ + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED: + info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING: + info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT); + break; + case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE: + info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK); + break; + default: + info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN); + break; + } + } + + mm_callback_info_schedule (info); +} + +static void +reg_status_updated (MMGenericGsm *self, int new_value) +{ + MMModemGsmNetworkRegStatus status; + + switch (new_value) { + 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 (self, status); + + /* Stop the pending registration in case of success or certain failure */ + if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME || + status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING || + status == MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED) + + pending_registration_stop (self); +} + +static void +reg_state_changed (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data) +{ + char *str; + + str = g_match_info_fetch (match_info, 1); + reg_status_updated (MM_GENERIC_GSM (serial), atoi (str)); + g_free (str); +} + 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); + + if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration) get_registration_status (MM_SERIAL (info->modem), info); - } - return TRUE; + return FALSE; } static void -reg_status_remove (gpointer data) +reg_status_again_remove (gpointer data) { - g_source_remove (GPOINTER_TO_UINT (data)); + g_source_remove (GPOINTER_TO_INT (data)); } static void @@ -531,90 +652,37 @@ get_reg_status_done (MMSerial *serial, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - const char *reply = response->str; - guint32 id; - gboolean done = FALSE; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial); if (error) { info->error = g_error_copy (error); - goto out; + pending_registration_done (info); + return; } - if (g_str_has_prefix (reply, "+CREG: ")) { + if (g_str_has_prefix (response->str, "+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 */ - done = TRUE; - break; - case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE: - /* Huh? Stupid card, we told it to register, pretend it returned SEARCHING - (hoping it will eventually start searching) */ - /* fall through */ - 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; + int unsolicited, stat; + + if (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat)) { + reg_status_updated (MM_GENERIC_GSM (serial), stat); + + if (!unsolicited && priv->pending_registration) { + guint id; + + id = g_timeout_add_seconds (1, reg_status_again, info); + mm_callback_info_set_data (info, "reg-status-again", + GINT_TO_POINTER (id), + reg_status_again_remove); } } } else { - g_debug ("unknown response: %s", reply); + g_debug ("unknown response: %s", response->str); info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Could not parse the response"); + pending_registration_done (info); } - - out: - if (done || info->error) - mm_callback_info_schedule (info); } static void @@ -629,13 +697,8 @@ register_done (MMSerial *serial, GError *error, gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } else - get_registration_status (serial, info); + /* Ignore errors here, get the actual registration status */ + get_registration_status (serial, (MMCallbackInfo *) user_data); } static void @@ -649,12 +712,18 @@ do_register (MMModemGsmNetwork *modem, info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_registration = + g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 60, + pending_registration_timed_out, + info, + pending_registration_done); + if (network_id) command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id); else command = g_strdup ("+COPS=0,,"); - mm_serial_queue_command (MM_SERIAL (modem), command, 60, register_done, info); + mm_serial_queue_command (MM_SERIAL (modem), command, 5, register_done, info); g_free (command); } @@ -1366,10 +1435,16 @@ modem_simple_init (MMModemSimple *class) static void mm_generic_gsm_init (MMGenericGsm *self) { + GRegex *regex; + mm_serial_set_response_parser (MM_SERIAL (self), mm_serial_parser_v1_parse, mm_serial_parser_v1_new (), mm_serial_parser_v1_destroy); + + regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, reg_state_changed, NULL, NULL); + g_regex_unref (regex); } static void @@ -1432,6 +1507,8 @@ finalize (GObject *object) { MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object); + pending_registration_stop (MM_GENERIC_GSM (object)); + g_free (priv->driver); g_free (priv->data_device); g_free (priv->oper_code); diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index ab8bee07..d51f734d 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -26,6 +26,9 @@ GType mm_generic_gsm_get_type (void); MMModem *mm_generic_gsm_new (const char *serial_device, const char *driver); +void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem, + gboolean enabled); + void mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid); diff --git a/src/mm-serial.c b/src/mm-serial.c index c81ca70d..3bd2b0d6 100644 --- a/src/mm-serial.c +++ b/src/mm-serial.c @@ -49,6 +49,7 @@ typedef struct { MMSerialResponseParserFn response_parser_fn; gpointer response_parser_user_data; GDestroyNotify response_parser_notify; + GSList *unsolicited_msg_handlers; struct termios old_t; @@ -65,6 +66,13 @@ typedef struct { guint timeout_id; } MMSerialPrivate; +typedef struct { + GRegex *regex; + MMSerialUnsolicitedMsgFn callback; + gpointer user_data; + GDestroyNotify notify; +} MMUnsolicitedMsgHandler; + const char * mm_serial_get_device (MMSerial *serial) { @@ -436,6 +444,29 @@ mm_serial_queue_process (gpointer data) } void +mm_serial_add_unsolicited_msg_handler (MMSerial *self, + GRegex *regex, + MMSerialUnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify) +{ + MMUnsolicitedMsgHandler *handler; + MMSerialPrivate *priv; + + g_return_if_fail (MM_IS_SERIAL (self)); + g_return_if_fail (regex != NULL); + + handler = g_slice_new (MMUnsolicitedMsgHandler); + handler->regex = g_regex_ref (regex); + handler->callback = callback; + handler->user_data = user_data; + handler->notify = notify; + + priv = MM_SERIAL_GET_PRIVATE (self); + priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler); +} + +void mm_serial_set_response_parser (MMSerial *self, MMSerialResponseParserFn fn, gpointer user_data, @@ -454,6 +485,58 @@ mm_serial_set_response_parser (MMSerial *self, } 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 FALSE; +} + +static void +parse_unsolicited_messages (MMSerial *self, + GString *response) +{ + MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) { + MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data; + GMatchInfo *match_info; + gboolean matches; + + matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL); + if (handler->callback) { + while (g_match_info_matches (match_info)) { + handler->callback (self, match_info, handler->user_data); + g_match_info_next (match_info, NULL); + } + } + + g_match_info_free (match_info); + + if (matches) { + /* Remove matches */ + char *str; + int result_len = response->len; + + str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0, + remove_eval_cb, &result_len, NULL); + + g_string_truncate (response, 0); + g_string_append_len (response, str, result_len); + g_free (str); + } + } +} + +static gboolean parse_response (MMSerial *self, GString *response, GError **error) @@ -462,6 +545,8 @@ parse_response (MMSerial *self, g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE); + parse_unsolicited_messages (self, response); + return priv->response_parser_fn (priv->response_parser_user_data, response, error); } @@ -841,6 +926,18 @@ finalize (GObject *object) g_string_free (priv->response, TRUE); g_free (priv->device); + while (priv->unsolicited_msg_handlers) { + MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data; + + if (handler->notify) + handler->notify (handler->user_data); + + g_regex_unref (handler->regex); + g_slice_free (MMUnsolicitedMsgHandler, handler); + priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers, + priv->unsolicited_msg_handlers); + } + if (priv->response_parser_notify) priv->response_parser_notify (priv->response_parser_user_data); diff --git a/src/mm-serial.h b/src/mm-serial.h index 7d9e6961..e5e3d613 100644 --- a/src/mm-serial.h +++ b/src/mm-serial.h @@ -3,6 +3,7 @@ #ifndef MM_SERIAL_H #define MM_SERIAL_H +#include <glib.h> #include <glib/gtypes.h> #include <glib-object.h> @@ -28,6 +29,10 @@ typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data, GString *response, GError **error); +typedef void (*MMSerialUnsolicitedMsgFn) (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data); + typedef void (*MMSerialResponseFn) (MMSerial *serial, GString *response, GError *error, @@ -46,6 +51,12 @@ struct _MMSerialClass { GType mm_serial_get_type (void); +void mm_serial_add_unsolicited_msg_handler (MMSerial *self, + GRegex *regex, + MMSerialUnsolicitedMsgFn callback, + gpointer user_data, + GDestroyNotify notify); + void mm_serial_set_response_parser (MMSerial *self, MMSerialResponseParserFn fn, gpointer user_data, diff --git a/src/mm-util.c b/src/mm-util.c deleted file mode 100644 index 46badcf3..00000000 --- a/src/mm-util.c +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -#include "mm-util.h" - -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 FALSE; -} - -void -mm_util_strip_string (GString *string, - GRegex *regex, - MMUtilStripFn callback, - gpointer user_data) -{ - GMatchInfo *match_info; - gboolean matches; - char *str; - - g_return_if_fail (string != NULL); - g_return_if_fail (regex != NULL); - - matches = g_regex_match_full (regex, string->str, string->len, 0, 0, &match_info, NULL); - if (callback) { - while (g_match_info_matches (match_info)) { - str = g_match_info_fetch (match_info, 1); - callback (str, user_data); - g_free (str); - - g_match_info_next (match_info, NULL); - } - } - - g_match_info_free (match_info); - - if (matches) { - /* Remove matches */ - int result_len = string->len; - - str = g_regex_replace_eval (regex, 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); - } -} diff --git a/src/mm-util.h b/src/mm-util.h deleted file mode 100644 index 049b5f94..00000000 --- a/src/mm-util.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -#ifndef MM_UTIL_H -#define MM_UTIL_H - -#include <glib.h> - -typedef void (*MMUtilStripFn) (const char *str, - gpointer user_data); - -/* Applies the regexp on string and calls the callback (if provided) - with each match and user_data. After that, the matches are removed - from the string. -*/ -void mm_util_strip_string (GString *string, - GRegex *regex, - MMUtilStripFn callback, - gpointer user_data); - -#endif /* MM_UTIL_H */ |