From 783de3bc10ba8b6fe934887efaeb229da6ad9d06 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 09:55:04 -0800 Subject: [PATCH 01/18] core: fix SIGTERM before mainloop has started --- src/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 36691150..916acb36 100644 --- a/src/main.c +++ b/src/main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include "mm-manager.h" @@ -33,8 +34,11 @@ mm_signal_handler (int signo) mm_options_set_debug (!mm_options_debug ()); else if (signo == SIGINT || signo == SIGTERM) { g_message ("Caught signal %d, shutting down...", signo); - g_main_loop_quit (loop); - } + if (loop) + g_main_loop_quit (loop); + else + _exit (0); + } } static void From 2f925599c089484286a08ce84885b0385bcd64d2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 10:20:32 -0800 Subject: [PATCH 02/18] gsm: fix crash in Simple API's GetStatus handling Can't schedule the info completion until we're sure all the requests have completed. They won't necessarily be completed in the same order they were issued since some of the data the requests pull from could be cached and thus we don't have to wait in the queue to hit up the modem. --- src/mm-generic-gsm.c | 71 ++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index b2794e21..53406b15 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -2504,8 +2504,6 @@ simple_connect (MMModemSimple *simple, simple_state_machine (MM_MODEM (simple), NULL, info); } - - static void simple_free_gvalue (gpointer data) { @@ -2537,16 +2535,39 @@ simple_string_value (const char *str) return val; } +#define NOTDONE_TAG "not-done" +#define SS_HASH_TAG "simple-get-status" + +static void +simple_status_complete_item (MMCallbackInfo *info) +{ + guint32 completed = GPOINTER_TO_UINT (mm_callback_info_get_data (info, NOTDONE_TAG)); + + g_warn_if_fail (completed > 0); + + /* Decrement the number of outstanding calls and if there aren't any left, + * schedule the callback info completion. + */ + completed--; + mm_callback_info_set_data (info, NOTDONE_TAG, GUINT_TO_POINTER (completed), NULL); + if (completed == 0) + mm_callback_info_schedule (info); +} + static void simple_status_got_signal_quality (MMModem *modem, guint32 result, GError *error, gpointer user_data) { - if (error) - g_warning ("Error getting signal quality: %s", error->message); - else - g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result)); + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GHashTable *properties; + + if (!error) { + properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG); + g_hash_table_insert (properties, "signal_quality", simple_uint_value (result)); + } + simple_status_complete_item (info); } static void @@ -2555,9 +2576,14 @@ simple_status_got_band (MMModem *modem, GError *error, gpointer user_data) { - /* Ignore band errors since there's no generic implementation for it */ - if (!error) - g_hash_table_insert ((GHashTable *) user_data, "band", simple_uint_value (result)); + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GHashTable *properties; + + if (!error) { + properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG); + g_hash_table_insert (properties, "band", simple_uint_value (result)); + } + simple_status_complete_item (info); } static void @@ -2571,17 +2597,15 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem, MMCallbackInfo *info = (MMCallbackInfo *) user_data; GHashTable *properties; - if (error) - info->error = g_error_copy (error); - else { - properties = (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"); - + info->error = mm_modem_check_removed ((MMModem *) modem, error); + if (!info->error) { + properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG); + g_hash_table_insert (properties, "registration_status", simple_uint_value (status)); g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code)); g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name)); } - - mm_callback_info_schedule (info); + simple_status_complete_item (info); } static void @@ -2590,7 +2614,7 @@ simple_get_status_invoke (MMCallbackInfo *info) MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback; callback (MM_MODEM_SIMPLE (info->modem), - (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"), + (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG), info->error, info->user_data); } @@ -2610,12 +2634,15 @@ simple_get_status (MMModemSimple *simple, G_CALLBACK (callback), user_data); - properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue); - mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref); + properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue); + mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref); - mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, properties); - mm_modem_gsm_network_get_band (gsm, simple_status_got_band, properties); - mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, properties); + mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, info); + mm_modem_gsm_network_get_band (gsm, simple_status_got_band, info); + mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, info); + + /* 3 calls to complete before scheduling the callback: (signal, band, reginfo) */ + mm_callback_info_set_data (info, NOTDONE_TAG, GUINT_TO_POINTER (3), NULL); if (priv->act > -1) { /* Deprecated key */ From 076b5df1bcc880555606375ac2af9db488bfda3d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 11:29:04 -0800 Subject: [PATCH 03/18] gsm: fix inifinite in ETSI tech -> MM tech conversion function --- src/mm-generic-gsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 53406b15..d1de512c 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -2216,6 +2216,7 @@ etsi_act_to_mm_act (gint act) while (iter->mm_act != MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) { if (iter->etsi_act == act) return iter->mm_act; + iter++; } return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; } From fbf3efc1e3feac3acb340bbd70617a702b33a42c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 11:43:30 -0800 Subject: [PATCH 04/18] gsm: only use CREG responses for authoritative registration state For now... --- src/mm-generic-gsm.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index d1de512c..9b693096 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -1475,14 +1475,21 @@ reg_state_changed (MMSerialPort *port, return; } - if (reg_status_updated (self, state, NULL)) { - /* If registration is finished (either registered or failed) but the - * registration query hasn't completed yet, just remove the timeout and - * let the registration query complete. - */ - if (priv->pending_reg_id) { - g_source_remove (priv->pending_reg_id); - priv->pending_reg_id = 0; + /* Don't update reg state on CGREG responses since for many devices it's + * unclear what that registration state that actually reflects. We'll + * take CGREG registration state into account later when we have a more + * consistent way of handling it. + */ + if (cgreg == FALSE) { + if (reg_status_updated (self, state, NULL)) { + /* If registration is finished (either registered or failed) but the + * registration query hasn't completed yet, just remove the timeout and + * let the registration query complete. + */ + if (priv->pending_reg_id) { + g_source_remove (priv->pending_reg_id); + priv->pending_reg_id = 0; + } } } From 81dc2dde459ca91a8fce3c19a3aec79734179dfa Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 11:44:25 -0800 Subject: [PATCH 05/18] serial: default to XON/XOFF flow control This seems to help with Huawei and ZTE devices which often appeared to stop responding on either primary or secondary ports at various times. We had this problem a long time ago, but it was fixed then by always picking the right serial port via the USB interface number (Huawei) or udev rules files (ZTE). Now that we're using the second serial port more extensively the problem came up again, so lets try to fix it for real. --- src/mm-generic-gsm.c | 3 +++ src/mm-serial-port.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 9b693096..8d013a52 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -698,6 +698,9 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, } } + /* Try to enable XON/XOFF flow control */ + mm_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL); + /* Get allowed mode */ if (MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode) MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode (self, get_allowed_mode_done, NULL); diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index 07010e1d..1d98a537 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -357,8 +357,14 @@ config_fd (MMSerialPort *self, GError **error) stbuf.c_cc[VTIME] = 0; stbuf.c_cc[VEOF] = 1; - stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); - stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); + /* Use software handshaking */ + stbuf.c_iflag |= (IXON | IXOFF | IXANY); + + /* Set up port speed and serial attributes; also ignore modem control + * lines since most drivers don't implement RTS/CTS anyway. + */ + stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS); + stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits | CLOCAL); if (ioctl (priv->fd, TCSETA, &stbuf) < 0) { g_set_error (error, From d01a3ae328afe22552933b1d297b3cf92475b9de Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 11:47:13 -0800 Subject: [PATCH 06/18] cdma: tell the modem we're using XON/XOFF too --- src/mm-generic-cdma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 50cd86cb..ff156412 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -358,6 +358,9 @@ enable_all_done (MMModem *modem, GError *error, gpointer user_data) if (error) info->error = g_error_copy (error); else { + /* Try to enable XON/XOFF flow control */ + mm_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL); + /* Open up the second port, if one exists */ if (priv->secondary) { if (!mm_serial_port_open (priv->secondary, &info->error)) { From 07fc116d5a16b1f168c120c298b747b9d9bbe8d8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 18:10:34 -0800 Subject: [PATCH 07/18] api: add USSD API proposal Loosely based on oFono, from Pablo Marti. --- introspection/Makefile.am | 3 +- introspection/all.xml | 1 + introspection/mm-modem-gsm-ussd.xml | 73 +++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 introspection/mm-modem-gsm-ussd.xml diff --git a/introspection/Makefile.am b/introspection/Makefile.am index 175162cc..941c9244 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -14,4 +14,5 @@ EXTRA_DIST = \ mm-modem-gsm-sms.xml \ mm-modem-simple.xml \ mm-serial-error.xml \ - mm-modem-location.xml + mm-modem-location.xml \ + mm-modem-gsm-ussd.xml diff --git a/introspection/all.xml b/introspection/all.xml index 30725c73..967e90da 100644 --- a/introspection/all.xml +++ b/introspection/all.xml @@ -33,6 +33,7 @@ + diff --git a/introspection/mm-modem-gsm-ussd.xml b/introspection/mm-modem-gsm-ussd.xml new file mode 100644 index 00000000..f1f99fb4 --- /dev/null +++ b/introspection/mm-modem-gsm-ussd.xml @@ -0,0 +1,73 @@ + + + + + + + + Sends a USSD command string to the network initiating a USSD session. + When the request is handled by the network, the method returns the + response or an appropriate error. The network may be awaiting further + response from the ME after returning from this method and no new command + can be initiated until this one is cancelled or ended. + + + + + + The command to start the USSD session with. + + + + + + + Respond to a USSD request that is either initiated by the mobile network, + or that is awaiting further input after Initiate() was called. + + + + + + The response to network-initiated USSD command, or a response to a + request for further input. + + + + + + + Cancel an ongoing USSD session, either mobile or network initiated. + + + + + + + + Indicates the state of any ongoing USSD session. Values may be one of + the following: "idle" (no active session), "active" (a session is active + and the mobile is waiting for a response), "user-response" (the network + is waiting for the client's response, which must be sent using Respond()). + + + + + + Contains any network-initiated request to which no USSD response is + required. When no USSD session is active, or when there is no network- + initiated request, this property will be a zero-length string. + + + + + + Contains any pending network-initiated request for a response. Client + should call Respond() with the appropriate response to this request. + When no USSD session is active, or when there is no pending + network-initiated request, this property will be a zero-length string. + + + + + From 38514db896ea8706375c222e1262fa10b830c4c6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 21:31:57 -0800 Subject: [PATCH 08/18] gsm: let generic class handle signal quality caching and updates --- plugins/mm-modem-huawei-gsm.c | 39 ++--------- plugins/mm-modem-mbm.c | 14 ++-- src/mm-generic-gsm.c | 123 ++++++++++++++++++++++++++++------ src/mm-generic-gsm.h | 5 ++ 4 files changed, 121 insertions(+), 60 deletions(-) diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c index 67d27920..b0d6a16e 100644 --- a/plugins/mm-modem-huawei-gsm.c +++ b/plugins/mm-modem-huawei-gsm.c @@ -40,7 +40,6 @@ G_DEFINE_TYPE_EXTENDED (MMModemHuaweiGsm, mm_modem_huawei_gsm, MM_TYPE_GENERIC_G typedef struct { /* Cached state */ - guint signal_quality; MMModemGsmBand band; } MMModemHuaweiGsmPrivate; @@ -369,29 +368,6 @@ get_band (MMModemGsmNetwork *modem, } } -static void -get_signal_quality (MMModemGsmNetwork *modem, - MMModemUIntFn callback, - gpointer user_data) -{ - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); - - if (priv->signal_quality) { - /* have cached signal quality (from an unsolicited message). Use that */ - MMCallbackInfo *info; - - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL); - mm_callback_info_schedule (info); - } else { - /* Use the generic implementation */ - MMModemGsmNetwork *parent_gsm_network_iface; - - parent_gsm_network_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (modem)); - parent_gsm_network_iface->get_signal_quality (modem, callback, user_data); - } -} - /* Unsolicited message handlers */ static void @@ -400,24 +376,22 @@ handle_signal_quality_change (MMSerialPort *port, gpointer user_data) { MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data); - MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); char *str; - int quality; + int quality = 0; str = g_match_info_fetch (match_info, 1); quality = atoi (str); g_free (str); - if (quality == 99) + if (quality == 99) { /* 99 means unknown */ quality = 0; - else + } else { /* Normalize the quality */ - quality = quality * 100 / 31; + quality = CLAMP (quality, 0, 31) * 100 / 31; + } - g_debug ("Signal quality: %d", quality); - priv->signal_quality = (guint32) quality; - mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), (guint32) quality); + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (self), (guint32) quality); } static void @@ -568,7 +542,6 @@ modem_gsm_network_init (MMModemGsmNetwork *class) { class->set_band = set_band; class->get_band = get_band; - class->get_signal_quality = get_signal_quality; } static void diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index ccf20d85..ed39cec4 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -561,7 +561,7 @@ mbm_ciev_received (MMSerialPort *port, str = g_match_info_fetch (info, 2); if (str) { quality = atoi (str); - mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (user_data), quality * 20); + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), quality * 20); } } } @@ -790,17 +790,19 @@ grab_port (MMModem *modem, } port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + if (port && MM_IS_SERIAL_PORT (port)) { GRegex *regex; + if (ptype == MM_PORT_TYPE_PRIMARY) { + regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL); + g_regex_unref (regex); + } + regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_emrdy_received, modem, NULL); g_regex_unref (regex); - regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL); - g_regex_unref (regex); - regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL); g_regex_unref (regex); diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 8d013a52..5d209366 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -78,6 +78,8 @@ typedef struct { guint pending_reg_id; MMCallbackInfo *pending_reg_info; + guint signal_quality_id; + time_t signal_quality_timestamp; guint32 signal_quality; guint32 cid; @@ -144,14 +146,6 @@ 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) -{ -} - typedef struct { const char *result; const char *normalized; @@ -905,6 +899,11 @@ disable (MMModem *modem, priv->poll_id = 0; } + if (priv->signal_quality_id) { + g_source_remove (priv->signal_quality_id); + priv->signal_quality_id = 0; + } + priv->lac[0] = 0; priv->lac[1] = 0; priv->cell_id[0] = 0; @@ -1361,6 +1360,15 @@ mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem) } } +static void +got_signal_quality (MMModem *modem, + guint32 quality, + GError *error, + gpointer user_data) +{ + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (modem), quality); +} + void mm_generic_gsm_set_reg_status (MMGenericGsm *modem, MMModemGsmNetworkRegStatus status) @@ -2138,26 +2146,91 @@ set_apn (MMModemGsmNetwork *modem, /* GetSignalQuality */ +static gboolean +emit_signal_quality_change (gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (user_data); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + priv->signal_quality_id = 0; + priv->signal_quality_timestamp = time (NULL); + mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), priv->signal_quality); + return FALSE; +} + +void +mm_generic_gsm_update_signal_quality (MMGenericGsm *self, guint32 quality) +{ + MMGenericGsmPrivate *priv; + guint delay = 0; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_GENERIC_GSM (self)); + g_return_if_fail (quality <= 100); + + priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + if (priv->signal_quality == quality) + return; + + priv->signal_quality = quality; + + /* Some modems will send unsolcited signal quality changes quite often, + * so rate-limit them to every few seconds. Track the last time we + * emitted signal quality so that we send the signal immediately if there + * haven't been any updates in a while. + */ + if (!priv->signal_quality_id) { + if (priv->signal_quality_timestamp > 0) { + time_t curtime; + long int diff; + + curtime = time (NULL); + diff = curtime - priv->signal_quality_timestamp; + if (diff == 0) { + /* If the device is sending more than one update per second, + * make sure we don't spam clients with signals. + */ + delay = 3; + } else if ((diff > 0) && (diff <= 3)) { + /* Emitted an update less than 3 seconds ago; schedule an update + * 3 seconds after the previous one. + */ + delay = (guint) diff; + } else { + /* Otherwise, we haven't emitted an update in the last 3 seconds, + * or the user turned their clock back, or something like that. + */ + delay = 0; + } + } + + priv->signal_quality_id = g_timeout_add_seconds (delay, + emit_signal_quality_change, + self); + } +} + static void get_signal_quality_done (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { - MMGenericGsmPrivate *priv; MMCallbackInfo *info = (MMCallbackInfo *) user_data; char *reply = response->str; + gboolean parsed = FALSE; - if (error) - info->error = g_error_copy (error); - else if (!strncmp (reply, "+CSQ: ", 6)) { + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) + goto done; + + if (!strncmp (reply, "+CSQ: ", 6)) { /* Got valid reply */ int quality; int ber; - reply += 6; - - if (sscanf (reply, "%d, %d", &quality, &ber)) { + if (sscanf (reply + 6, "%d, %d", &quality, &ber)) { /* 99 means unknown */ if (quality == 99) { info->error = g_error_new_literal (MM_MOBILE_ERROR, @@ -2167,15 +2240,19 @@ get_signal_quality_done (MMSerialPort *port, /* Normalize the quality */ quality = CLAMP (quality, 0, 31) * 100 / 31; - priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); - priv->signal_quality = quality; + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (info->modem), quality); mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); } - } else - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Could not parse signal quality results"); + parsed = TRUE; + } } + if (!parsed && !info->error) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse signal quality results"); + } + +done: mm_callback_info_schedule (info); } @@ -2190,7 +2267,6 @@ get_signal_quality (MMModemGsmNetwork *modem, connected = mm_port_get_connected (MM_PORT (priv->primary)); if (connected && !priv->secondary) { - g_message ("Returning saved signal quality %d", priv->signal_quality); callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data); return; } @@ -2824,6 +2900,11 @@ finalize (GObject *object) priv->poll_id = 0; } + if (priv->signal_quality_id) { + g_source_remove (priv->signal_quality_id); + priv->signal_quality_id = 0; + } + mm_gsm_creg_regex_destroy (priv->reg_regex); g_free (priv->oper_code); diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index 13c996be..348ff4f8 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -133,6 +133,11 @@ void mm_generic_gsm_update_allowed_mode (MMGenericGsm *modem, void mm_generic_gsm_update_access_technology (MMGenericGsm *modem, MMModemGsmAccessTech act); +/* Called to asynchronously update the current signal quality of the device; + * 'quality' is a 0 - 100% quality. + */ +void mm_generic_gsm_update_signal_quality (MMGenericGsm *modem, guint32 quality); + void mm_generic_gsm_check_pin (MMGenericGsm *modem, MMModemFn callback, gpointer user_data); From 59605936d0e7d5947d1b888778fd993454db3b07 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 21:55:19 -0800 Subject: [PATCH 09/18] mbm: use unsolicited access technology updates --- plugins/mm-modem-mbm.c | 106 +++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 58 deletions(-) diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index ed39cec4..afb7d121 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -52,11 +52,6 @@ G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0, #define MBM_NETWORK_MODE_2G 5 #define MBM_NETWORK_MODE_3G 6 -#define MBM_ERINFO_2G_GPRS 1 -#define MBM_ERINFO_2G_EGPRS 2 -#define MBM_ERINFO_3G_UMTS 1 -#define MBM_ERINFO_3G_HSDPA 2 - typedef struct { guint reg_id; gboolean have_emrdy; @@ -129,9 +124,6 @@ register_done (gpointer user_data) primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_serial_port_queue_command (primary, "+CREG=1", 3, NULL, NULL); - mm_serial_port_queue_command (primary, "+CMER=3,0,0,1", 3, NULL, NULL); - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)); parent_modem_iface->do_register (MM_MODEM_GSM_NETWORK (self), reg_data->network_id, @@ -214,57 +206,47 @@ set_allowed_mode (MMGenericGsm *gsm, g_free (command); } -#if 0 static void -get_network_mode_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +mbm_erinfo_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *erinfo; - int mode = 0, gsm = 0, umts = 0; - gboolean parsed = FALSE; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; - if (error) { - info->error = g_error_copy (error); - goto done; + str = g_match_info_fetch (info, 2); + if (str) { + switch (atoi (str)) { + case 1: + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + break; + case 2: + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + break; + default: + break; + } } + g_free (str); - erinfo = strstr (response->str, "*ERINFO:"); - if (!erinfo) - goto done; - - if (sscanf (erinfo + 8, "%d,%d,%d", &mode, &gsm, &umts) != 3) - goto done; - - if (gsm || umts) { - MMModemGsmMode mm_mode = MM_MODEM_GSM_MODE_ANY; - - if (gsm == MBM_ERINFO_2G_GPRS) - mm_mode = MM_MODEM_GSM_MODE_GPRS; - else if (gsm == MBM_ERINFO_2G_EGPRS) - mm_mode = MM_MODEM_GSM_MODE_EDGE; - else if (umts == MBM_ERINFO_3G_UMTS) - mm_mode = MM_MODEM_GSM_MODE_UMTS; - else if (umts == MBM_ERINFO_3G_HSDPA) - mm_mode = MM_MODEM_GSM_MODE_HSDPA; - else - g_debug ("%s unknown network mode %d,%d", __FUNCTION__, gsm, umts); - - mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_mode), NULL); - parsed = TRUE; + /* 3G modes take precedence */ + str = g_match_info_fetch (info, 3); + if (str) { + switch (atoi (str)) { + case 1: + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + break; + case 2: + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + break; + default: + break; + } } + g_free (str); -done: - if (!error && !parsed) { - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Could not parse network mode results"); - } - - mm_callback_info_schedule (info); + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); } -#endif static void get_allowed_mode_done (MMSerialPort *port, @@ -364,6 +346,10 @@ mbm_enable_done (MMSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* Start unsolicited signal strength and access technology responses */ + mm_serial_port_queue_command (port, "+CMER=3,0,0,1", 3, NULL, NULL); + mm_serial_port_queue_command (port, "*ERINFO=1", 3, NULL, NULL); + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); } @@ -456,10 +442,10 @@ typedef struct { } DisableInfo; static void -disable_creg_cmer_done (MMSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +disable_unsolicited_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMModem *parent_modem_iface; @@ -486,8 +472,8 @@ disable (MMModem *modem, primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); g_assert (primary); - /* Turn off unsolicited +CIEV signal strength indicator */ - mm_serial_port_queue_command (primary, "+CREG=0;+CMER=0", 5, disable_creg_cmer_done, info); + /* Turn off unsolicited responses */ + mm_serial_port_queue_command (primary, "+CMER=0;*ERINFO=0", 5, disable_unsolicited_done, info); } static void @@ -813,12 +799,16 @@ grab_port (MMModem *modem, /* also consume unsolicited mbm messages we are not interested in them - see LP: #416418 */ regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R", G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\*ERINFO:\\s*(\\d),(\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_erinfo_received, modem, NULL); + g_regex_unref (regex); } return TRUE; From 82abd5595e41f8a2739590bfa52e687de98d9808 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 21:55:51 -0800 Subject: [PATCH 10/18] mbm: fix memory leak in unsolicited signal strength processing --- plugins/mm-modem-mbm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index afb7d121..0344b089 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -537,11 +537,12 @@ mbm_ciev_received (MMSerialPort *port, gpointer user_data) { int quality = 0, ind = 0; - const char *str; + char *str; str = g_match_info_fetch (info, 1); if (str) ind = atoi (str); + g_free (str); if (ind == MBM_SIGNAL_INDICATOR) { str = g_match_info_fetch (info, 2); @@ -549,6 +550,7 @@ mbm_ciev_received (MMSerialPort *port, quality = atoi (str); mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), quality * 20); } + g_free (str); } } From ce1c72152b3488c06e5f01aa6b0dbd9ffcb918a4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 21:56:57 -0800 Subject: [PATCH 11/18] mbm: fix memory leak in connection state processing --- plugins/mm-modem-mbm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index 0344b089..43302e9b 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -580,11 +580,12 @@ mbm_e2nap_received (MMSerialPort *port, gpointer user_data) { int state = 0; - const char *str; + char *str; str = g_match_info_fetch (info, 1); if (str) state = atoi (str); + g_free (str); if (MBM_E2NAP_DISCONNECTED == state) { g_debug ("%s: disconnected", __func__); From b0598738f1e99d3902a2aa79d5c3f74b686310f1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 22:12:21 -0800 Subject: [PATCH 12/18] gsm: only update access technology when enabled --- src/mm-generic-gsm.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 5d209366..a7149fd1 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -114,6 +114,9 @@ static gboolean handle_reg_status_response (MMGenericGsm *self, static MMModemGsmAccessTech etsi_act_to_mm_act (gint act); +static void _internal_update_access_technology (MMGenericGsm *modem, + MMModemGsmAccessTech act); + MMModem * mm_generic_gsm_new (const char *device, const char *driver, @@ -908,7 +911,7 @@ disable (MMModem *modem, priv->lac[1] = 0; priv->cell_id[0] = 0; priv->cell_id[1] = 0; - mm_generic_gsm_update_access_technology (self, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN); + _internal_update_access_technology (self, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN); /* Close the secondary port if its open */ if (priv->secondary && mm_serial_port_is_open (priv->secondary)) @@ -2307,15 +2310,14 @@ etsi_act_to_mm_act (gint act) return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; } -void -mm_generic_gsm_update_access_technology (MMGenericGsm *modem, - MMModemGsmAccessTech act) +static void +_internal_update_access_technology (MMGenericGsm *modem, + MMModemGsmAccessTech act) { MMGenericGsmPrivate *priv; g_return_if_fail (modem != NULL); g_return_if_fail (MM_IS_GENERIC_GSM (modem)); - g_return_if_fail (act >= MM_MODEM_GSM_ACCESS_TECH_UNKNOWN && act <= MM_MODEM_GSM_ACCESS_TECH_LAST); priv = MM_GENERIC_GSM_GET_PRIVATE (modem); @@ -2332,6 +2334,18 @@ mm_generic_gsm_update_access_technology (MMGenericGsm *modem, } } +void +mm_generic_gsm_update_access_technology (MMGenericGsm *self, + MMModemGsmAccessTech act) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_GENERIC_GSM (self)); + + /* For plugins, don't update the access tech when the modem isn't enabled */ + if (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_ENABLED) + _internal_update_access_technology (self, act); +} + void mm_generic_gsm_update_allowed_mode (MMGenericGsm *self, MMModemGsmAllowedMode mode) From 658d3d572dc5313d815d9b8ab09c6b563fcbc8bf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 22:12:46 -0800 Subject: [PATCH 13/18] zte: handle access technology changes --- plugins/mm-modem-zte.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index 663a728e..cb5f8054 100644 --- a/plugins/mm-modem-zte.c +++ b/plugins/mm-modem-zte.c @@ -52,6 +52,39 @@ mm_modem_zte_new (const char *device, NULL)); } +static void +zte_access_tech_changed (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + str = g_match_info_fetch (info, 1); + if (str) { + /* Better technologies are listed first since modem sometimes says + * stuff like "GPRS/EDGE" and that should be handled as EDGE. + */ + if (strstr (str, "HSPA")) + act = MM_MODEM_GSM_ACCESS_TECH_HSPA; + else if (strstr (str, "HSUPA")) + act = MM_MODEM_GSM_ACCESS_TECH_HSUPA; + else if (strstr (str, "HSDPA")) + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + else if (strstr (str, "UMTS")) + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (strstr (str, "EDGE")) + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + else if (strstr (str, "GPRS")) + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + else if (strstr (str, "GSM")) + act = MM_MODEM_GSM_ACCESS_TECH_GSM; + } + g_free (str); + + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); +} + /*****************************************************************************/ /* Modem class override functions */ /*****************************************************************************/ @@ -223,8 +256,8 @@ grab_port (MMModem *modem, g_regex_unref (regex); /* Current network and service domain */ - regex = g_regex_new ("\\r\\n\\+ZPASR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + regex = g_regex_new ("\\r\\n\\+ZPASR:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, zte_access_tech_changed, modem, NULL); g_regex_unref (regex); /* SIM request to Build Main Menu */ From 46ec3bdb47cbc0c40f39bb139783dedc5d3c9986 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 9 Mar 2010 23:34:19 -0800 Subject: [PATCH 14/18] gsm: fix CREG/CGREG parsing with multiple responses When a modem sends both CREG and CGREG in the same response packet, the parser was failing to correctly distinguish which response was being parsed, since the string passed to g_regex_match() is the whole response including both CREG + CGREG. --- src/mm-modem-helpers.c | 67 +++++++++++++++++----------------- src/mm-modem-helpers.h | 2 +- src/tests/test-modem-helpers.c | 22 +++++++++++ 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 56834d6d..e835eb9b 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -205,22 +205,22 @@ mm_gsm_destroy_scan_data (gpointer data) /*************************************************************************/ /* +CREG: (GSM 07.07 CREG=1 unsolicited) */ -#define CREG1 "\\+CG?REG:\\s*(\\d{1})" +#define CREG1 "\\+(CREG|CGREG):\\s*(\\d{1})" /* +CREG: , (GSM 07.07 CREG=1 solicited) */ -#define CREG2 "\\+CG?REG:\\s*(\\d{1}),\\s*(\\d{1})" +#define CREG2 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})" /* +CREG: ,, (GSM 07.07 CREG=2 unsolicited) */ -#define CREG3 "\\+CG?REG:\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" +#define CREG3 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" /* +CREG: ,,, (GSM 07.07 solicited and some CREG=2 unsolicited) */ -#define CREG4 "\\+CG?REG:\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" +#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" /* +CREG: ,,, (ETSI 27.007 CREG=2 unsolicited) */ -#define CREG5 "\\+CG?REG:\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})" +#define CREG5 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})" /* +CREG: ,,,, (ETSI 27.007 solicited and some CREG=2 unsolicited) */ -#define CREG6 "\\+CG?REG:\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})" +#define CREG6 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})" GPtrArray * mm_gsm_creg_regex_get (gboolean solicited) @@ -319,7 +319,7 @@ mm_gsm_parse_creg_response (GMatchInfo *info, gulong *out_lac, gulong *out_ci, gint *out_act, - gboolean *out_greg, + gboolean *out_cgreg, GError **error) { gboolean success = FALSE, foo; @@ -327,56 +327,60 @@ mm_gsm_parse_creg_response (GMatchInfo *info, gulong stat = 0, lac = 0, ci = 0; guint istat = 0, ilac = 0, ici = 0, iact = 0; char *str; - const char *orig_str; g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (out_reg_state != NULL, FALSE); g_return_val_if_fail (out_lac != NULL, FALSE); g_return_val_if_fail (out_ci != NULL, FALSE); g_return_val_if_fail (out_act != NULL, FALSE); - g_return_val_if_fail (out_greg != NULL, FALSE); + g_return_val_if_fail (out_cgreg != NULL, FALSE); + + str = g_match_info_fetch (info, 1); + if (str && strstr (str, "CGREG")) + *out_cgreg = TRUE; /* Normally the number of matches could be used to determine what each * item is, but we have overlap in one case. */ n_matches = g_match_info_get_match_count (info); - if (n_matches == 2) { + if (n_matches == 3) { /* CREG=1: +CREG: */ - istat = 1; - } else if (n_matches == 3) { - /* Solicited response: +CREG: , */ istat = 2; } else if (n_matches == 4) { - /* CREG=2 (GSM 07.07): +CREG: ,, */ - istat = 1; - ilac = 2; - ici = 3; + /* Solicited response: +CREG: , */ + istat = 3; } else if (n_matches == 5) { + /* CREG=2 (GSM 07.07): +CREG: ,, */ + istat = 2; + ilac = 3; + ici = 4; + } else if (n_matches == 6) { /* CREG=2 (ETSI 27.007): +CREG: ,,, * CREG=2 (non-standard): +CREG: ,,, */ - /* To distinguish, check length of the second match item. If it's - * more than one digit or has quotes in it, then we have the first format. + /* To distinguish, check length of the third match item. If it's + * more than one digit or has quotes in it then it's a LAC and we + * got the first format. */ - str = g_match_info_fetch (info, 2); + str = g_match_info_fetch (info, 3); if (str && (strchr (str, '"') || strlen (str) > 1)) { g_free (str); - istat = 1; - ilac = 2; - ici = 3; - iact = 4; - } else { istat = 2; ilac = 3; ici = 4; + iact = 5; + } else { + istat = 3; + ilac = 4; + ici = 5; } - } else if (n_matches == 6) { + } else if (n_matches == 7) { /* CREG=2 (non-standard): +CREG: ,,,, */ - istat = 2; - ilac = 3; - ici = 4; - iact = 5; + istat = 3; + ilac = 4; + ici = 5; + iact = 6; } /* Status */ @@ -416,9 +420,6 @@ mm_gsm_parse_creg_response (GMatchInfo *info, act = -1; } - orig_str = g_match_info_get_string (info); - *out_greg = !!strstr (orig_str, "+CGREG"); - *out_reg_state = (guint32) stat; if (stat != 4) { /* Don't fill in lac/ci/act if the device's state is unknown */ diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index 57956766..d8c74d84 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -36,7 +36,7 @@ gboolean mm_gsm_parse_creg_response (GMatchInfo *info, gulong *out_lac, gulong *out_ci, gint *out_act, - gboolean *out_greg, + gboolean *out_cgreg, GError **error); #endif /* MM_MODEM_HELPERS_H */ diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 980c4ae7..addeee3e 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -670,6 +670,25 @@ test_cgreg2_f3607gw_unsolicited (void *f, gpointer d) test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result); } +static void +test_creg_cgreg_multi_unsolicited (void *f, gpointer d) +{ + TestData *data = (TestData *) d; + const char *reply = "\r\n+CREG: 5\r\n\r\n+CGREG: 0\r\n"; + const CregResult result = { 5, 0, 0, -1, 1, FALSE}; + + test_creg_match ("Multi CREG/CGREG", FALSE, reply, data, &result); +} + +static void +test_creg_cgreg_multi2_unsolicited (void *f, gpointer d) +{ + TestData *data = (TestData *) d; + const char *reply = "\r\n+CGREG: 0\r\n\r\n+CREG: 5\r\n"; + const CregResult result = { 0, 0, 0, -1, 1, TRUE}; + + test_creg_match ("Multi CREG/CGREG #2", FALSE, reply, data, &result); +} static TestData * test_data_new (void) @@ -753,6 +772,9 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_solicited, data)); g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_unsolicited, data)); + g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi_unsolicited, data)); + g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi2_unsolicited, data)); + result = g_test_run (); test_data_free (data); From d2e69d34f86e8994f74fb209082fcfb7573b2ea2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 10 Mar 2010 00:02:56 -0800 Subject: [PATCH 15/18] gsm: make registration status change handling consistent Ensure we send out signals when anything changes. --- src/mm-generic-gsm.c | 118 ++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index a7149fd1..73a34d57 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -117,6 +117,14 @@ static MMModemGsmAccessTech etsi_act_to_mm_act (gint act); static void _internal_update_access_technology (MMGenericGsm *modem, MMModemGsmAccessTech act); +static void reg_info_updated (MMGenericGsm *self, + gboolean update_rs, + MMModemGsmNetworkRegStatus status, + gboolean update_code, + const char *oper_code, + gboolean update_name, + const char *oper_name); + MMModem * mm_generic_gsm_new (const char *device, const char *driver, @@ -841,13 +849,18 @@ disable_done (MMSerialPort *port, info->error = mm_modem_check_removed (info->modem, error); if (!info->error) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + MMGenericGsm *self = MM_GENERIC_GSM (info->modem); mm_serial_port_close (port); mm_modem_set_state (MM_MODEM (info->modem), MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_REASON_NONE); - priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN; + + /* Clear out registration info */ + reg_info_updated (self, + TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN, + TRUE, NULL, + TRUE, NULL); } mm_callback_info_schedule (info); } @@ -1268,6 +1281,49 @@ change_pin (MMModemGsmCard *modem, g_free (command); } +static void +reg_info_updated (MMGenericGsm *self, + gboolean update_rs, + MMModemGsmNetworkRegStatus status, + gboolean update_code, + const char *oper_code, + gboolean update_name, + const char *oper_name) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + gboolean changed = FALSE; + + if (update_rs) { + if (status != priv->reg_status) { + priv->reg_status = status; + changed = TRUE; + } + } + + if (update_code) { + if (g_strcmp0 (oper_code, priv->oper_code) != 0) { + g_free (priv->oper_code); + priv->oper_code = g_strdup (oper_code); + changed = TRUE; + } + } + + if (update_name) { + if (g_strcmp0 (oper_name, priv->oper_name) != 0) { + g_free (priv->oper_name); + priv->oper_name = g_strdup (oper_name); + changed = TRUE; + } + } + + if (changed) { + mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (self), + priv->reg_status, + priv->oper_code, + priv->oper_name); + } +} + static char * parse_operator (const char *reply) { @@ -1300,18 +1356,14 @@ read_operator_code_done (MMSerialPort *port, GError *error, gpointer user_data) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + MMGenericGsm *self = MM_GENERIC_GSM (user_data); char *oper; - if (error) - return; - - oper = parse_operator (response->str); - if (!oper) - return; - - g_free (priv->oper_code); - priv->oper_code = oper; + if (!error) { + oper = parse_operator (response->str); + if (oper) + reg_info_updated (self, FALSE, 0, TRUE, oper, FALSE, NULL); + } } static void @@ -1320,23 +1372,14 @@ read_operator_name_done (MMSerialPort *port, GError *error, gpointer user_data) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + MMGenericGsm *self = MM_GENERIC_GSM (user_data); char *oper; - if (error) - return; - - oper = parse_operator (response->str); - if (!oper) - return; - - g_free (priv->oper_name); - priv->oper_name = oper; - - mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (user_data), - priv->reg_status, - priv->oper_code, - priv->oper_name); + if (!error) { + oper = parse_operator (response->str); + if (oper) + reg_info_updated (self, FALSE, 0, FALSE, NULL, TRUE, oper); + } } /* Registration */ @@ -1392,14 +1435,8 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem, mm_serial_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem); mm_serial_port_queue_command (priv->primary, "+COPS=3,0;+COPS?", 3, read_operator_name_done, modem); 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); - priv->oper_code = priv->oper_name = NULL; - - mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (modem), priv->reg_status, - priv->oper_code, priv->oper_name); - } + } else + reg_info_updated (MM_GENERIC_GSM (modem), FALSE, 0, TRUE, NULL, TRUE, NULL); mm_generic_gsm_update_enabled_state (modem, TRUE, MM_MODEM_STATE_REASON_NONE); } @@ -1684,7 +1721,11 @@ registration_timed_out (gpointer data) g_warn_if_fail (info == priv->pending_reg_info); - priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE; + /* Clear out registration info */ + reg_info_updated (MM_GENERIC_GSM (info->modem), + TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE, + TRUE, NULL, + TRUE, NULL); info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT); mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (info->modem)); @@ -2705,6 +2746,11 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem, g_hash_table_insert (properties, "registration_status", simple_uint_value (status)); g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code)); g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name)); + + reg_info_updated (MM_GENERIC_GSM (info->modem), + TRUE, status, + TRUE, oper_code, + TRUE, oper_name); } simple_status_complete_item (info); } From 05dde9a3152368d40f1a82ece9d296e2ccbac428 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 10 Mar 2010 00:15:53 -0800 Subject: [PATCH 16/18] gsm: revert part of d2e69d34f86e8994f74fb209082fcfb7573b2ea2 Reg status returned here is cached reg status, so it's pointless to set it here again. --- src/mm-generic-gsm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 73a34d57..5a50b24a 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -2746,11 +2746,6 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem, g_hash_table_insert (properties, "registration_status", simple_uint_value (status)); g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code)); g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name)); - - reg_info_updated (MM_GENERIC_GSM (info->modem), - TRUE, status, - TRUE, oper_code, - TRUE, oper_name); } simple_status_complete_item (info); } From 611e832d0ed509f862c8ab047cd8faca98f5aec9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 10 Mar 2010 10:17:26 -0800 Subject: [PATCH 17/18] api: add 'reply' parameter to USSD Initiate() command --- introspection/mm-modem-gsm-ussd.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/introspection/mm-modem-gsm-ussd.xml b/introspection/mm-modem-gsm-ussd.xml index f1f99fb4..ae6884f0 100644 --- a/introspection/mm-modem-gsm-ussd.xml +++ b/introspection/mm-modem-gsm-ussd.xml @@ -18,6 +18,11 @@ The command to start the USSD session with. + + + The network response to the command which started the USSD session. + + From b7858ba235c046a514fbc79e18ac9faa75982032 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 10 Mar 2010 10:58:27 -0800 Subject: [PATCH 18/18] novatel: fix GSM secondary port enabling after PIN changes With the PIN changes, the primary port would already be closed by the time the Novatel modem class was able to send the DMAT command to enable the secondary ports. Just try again later. --- plugins/mm-modem-novatel-gsm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c index 4a846269..c8c28d1f 100644 --- a/plugins/mm-modem-novatel-gsm.c +++ b/plugins/mm-modem-novatel-gsm.c @@ -48,13 +48,27 @@ mm_modem_novatel_gsm_new (const char *device, /* Modem class override functions */ /*****************************************************************************/ +static void +dmat_callback2 (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_serial_port_close (port); +} + static void dmat_callback (MMSerialPort *port, GString *response, GError *error, gpointer user_data) { - mm_serial_port_close (port); + if (error) { + /* Try it again */ + if (mm_serial_port_open (port, NULL)) + mm_serial_port_queue_command (port, "$NWDMAT=1", 2, dmat_callback2, NULL); + } else + mm_serial_port_close (port); } static gboolean