diff options
-rw-r--r-- | introspection/Makefile.am | 3 | ||||
-rw-r--r-- | introspection/all.xml | 1 | ||||
-rw-r--r-- | introspection/mm-modem-gsm-ussd.xml | 78 | ||||
-rw-r--r-- | plugins/mm-modem-huawei-gsm.c | 39 | ||||
-rw-r--r-- | plugins/mm-modem-mbm.c | 131 | ||||
-rw-r--r-- | plugins/mm-modem-novatel-gsm.c | 16 | ||||
-rw-r--r-- | plugins/mm-modem-zte.c | 37 | ||||
-rw-r--r-- | src/main.c | 8 | ||||
-rw-r--r-- | src/mm-generic-cdma.c | 3 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 358 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 5 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 67 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 2 | ||||
-rw-r--r-- | src/mm-serial-port.c | 10 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 22 |
15 files changed, 545 insertions, 235 deletions
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 @@ <xi:include href="mm-modem-gsm-network.xml"/> <xi:include href="mm-modem-gsm-sms.xml"/> <xi:include href="mm-modem-gsm-hso.xml"/> + <xi:include href="mm-modem-gsm-ussd.xml"/> <xi:include href="mm-serial-error.xml"/> <xi:include href="mm-modem-error.xml"/> diff --git a/introspection/mm-modem-gsm-ussd.xml b/introspection/mm-modem-gsm-ussd.xml new file mode 100644 index 00000000..ae6884f0 --- /dev/null +++ b/introspection/mm-modem-gsm-ussd.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + + <interface name="org.freedesktop.ModemManager.Modem.Gsm.Ussd"> + <method name="Initiate"> + <tp:docstring> + 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. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_gsm_ussd_initiate"/> + <arg name="command" type="s" direction="in"> + <tp:docstring> + The command to start the USSD session with. + </tp:docstring> + </arg> + <arg name="reply" type="s" direction="out"> + <tp:docstring> + The network response to the command which started the USSD session. + </tp:docstring> + </arg> + </method> + + <method name="Respond"> + <tp:docstring> + Respond to a USSD request that is either initiated by the mobile network, + or that is awaiting further input after Initiate() was called. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_gsm_ussd_respond"/> + <arg name="response" type="s" direction="in"> + <tp:docstring> + The response to network-initiated USSD command, or a response to a + request for further input. + </tp:docstring> + </arg> + </method> + + <method name="Cancel"> + <tp:docstring> + Cancel an ongoing USSD session, either mobile or network initiated. + </tp:docstring> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_gsm_ussd_cancel"/> + </method> + + <property name="State" type="s" access="read"> + <tp:docstring> + 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()). + </tp:docstring> + </property> + + <property name="NetworkNotification" type="s" access="read"> + <tp:docstring> + 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. + </tp:docstring> + </property> + + <property name="NetworkRequest" type="s" access="read"> + <tp:docstring> + 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. + </tp:docstring> + </property> + + </interface> +</node> diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c index 78424863..b2677414 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 (MMAtSerialPort *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 f0e764aa..afc060ed 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_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); g_assert (primary); - mm_at_serial_port_queue_command (primary, "+CREG=1", 3, NULL, NULL); - mm_at_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 (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +mbm_erinfo_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) { - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - char *erinfo; - int mode = 0, gsm = 0, umts = 0; - gboolean parsed = FALSE; - - if (error) { - info->error = g_error_copy (error); - goto done; - } - - 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; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + + 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; + } } - -done: - if (!error && !parsed) { - info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, - "Could not parse network mode results"); + g_free (str); + + /* 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); - mm_callback_info_schedule (info); + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act); } -#endif static void get_allowed_mode_done (MMAtSerialPort *port, @@ -364,6 +346,10 @@ mbm_enable_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + /* Start unsolicited signal strength and access technology responses */ + mm_at_serial_port_queue_command (port, "+CMER=3,0,0,1", 3, NULL, NULL); + mm_at_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 (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMModem *parent_modem_iface; @@ -486,8 +472,8 @@ disable (MMModem *modem, primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); g_assert (primary); - /* Turn off unsolicited +CIEV signal strength indicator */ - mm_at_serial_port_queue_command (primary, "+CREG=0;+CMER=0", 5, disable_creg_cmer_done, info); + /* Turn off unsolicited responses */ + mm_at_serial_port_queue_command (primary, "+CMER=0;*ERINFO=0", 5, disable_unsolicited_done, info); } static void @@ -551,18 +537,20 @@ mbm_ciev_received (MMAtSerialPort *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); 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); } + g_free (str); } } @@ -592,11 +580,12 @@ mbm_e2nap_received (MMAtSerialPort *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__); @@ -791,17 +780,19 @@ grab_port (MMModem *modem, } port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_AT_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + if (port && MM_IS_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL); g_regex_unref (regex); @@ -812,12 +803,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_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, modem, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_erinfo_received, modem, NULL); + g_regex_unref (regex); } return TRUE; diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c index 61f933ac..72c929d3 100644 --- a/plugins/mm-modem-novatel-gsm.c +++ b/plugins/mm-modem-novatel-gsm.c @@ -49,12 +49,26 @@ mm_modem_novatel_gsm_new (const char *device, /*****************************************************************************/ static void +dmat_callback2 (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_serial_port_close (MM_SERIAL_PORT (port)); +} + +static void dmat_callback (MMAtSerialPort *port, GString *response, GError *error, gpointer user_data) { - mm_serial_port_close (MM_SERIAL_PORT (port)); + if (error) { + /* Try it again */ + if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL)) + mm_at_serial_port_queue_command (port, "$NWDMAT=1", 2, dmat_callback2, NULL); + } else + mm_serial_port_close (MM_SERIAL_PORT (port)); } static gboolean diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index d4418000..a4abe47b 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 (MMAtSerialPort *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_at_serial_port_add_unsolicited_msg_handler (MM_AT_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, zte_access_tech_changed, modem, NULL); g_regex_unref (regex); /* SIM request to Build Main Menu */ @@ -17,6 +17,7 @@ #include <signal.h> #include <syslog.h> #include <string.h> +#include <unistd.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #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 diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 8ec71a7a..4d558c3e 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_at_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 (MM_SERIAL_PORT (priv->secondary), &info->error)) { diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index fd4d8380..c6add593 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -79,6 +79,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; @@ -113,6 +115,17 @@ 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); + +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, @@ -145,14 +158,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; @@ -699,6 +704,9 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, } } + /* Try to enable XON/XOFF flow control */ + mm_at_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); @@ -842,13 +850,18 @@ disable_done (MMAtSerialPort *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 (MM_SERIAL_PORT (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); } @@ -903,11 +916,16 @@ 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; 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 (MM_SERIAL_PORT (priv->secondary))) @@ -1268,6 +1286,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 +1361,14 @@ read_operator_code_done (MMAtSerialPort *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 +1377,14 @@ read_operator_name_done (MMAtSerialPort *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 */ @@ -1363,6 +1411,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) @@ -1383,14 +1440,8 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem, mm_at_serial_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem); mm_at_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); } @@ -1480,14 +1531,21 @@ reg_state_changed (MMAtSerialPort *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; + } } } @@ -1668,7 +1726,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)); @@ -2133,26 +2195,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 (MMAtSerialPort *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, @@ -2162,15 +2289,19 @@ get_signal_quality_done (MMAtSerialPort *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); } @@ -2185,7 +2316,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; } @@ -2221,19 +2351,19 @@ 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; } -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); @@ -2251,6 +2381,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) { @@ -2509,8 +2651,6 @@ simple_connect (MMModemSimple *simple, simple_state_machine (MM_MODEM (simple), NULL, info); } - - static void simple_free_gvalue (gpointer data) { @@ -2542,16 +2682,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 @@ -2560,9 +2723,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 @@ -2576,17 +2744,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 @@ -2595,7 +2761,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); } @@ -2615,12 +2781,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, 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); - 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); + /* 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 */ @@ -2791,6 +2960,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 bf2b4576..bce04331 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); 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: <stat> (GSM 07.07 CREG=1 unsolicited) */ -#define CREG1 "\\+CG?REG:\\s*(\\d{1})" +#define CREG1 "\\+(CREG|CGREG):\\s*(\\d{1})" /* +CREG: <n>,<stat> (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: <stat>,<lac>,<ci> (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: <n>,<stat>,<lac>,<ci> (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: <stat>,<lac>,<ci>,<AcT> (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: <n>,<stat>,<lac>,<ci>,<AcT> (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: <stat> */ - istat = 1; - } else if (n_matches == 3) { - /* Solicited response: +CREG: <n>,<stat> */ istat = 2; } else if (n_matches == 4) { - /* CREG=2 (GSM 07.07): +CREG: <stat>,<lac>,<ci> */ - istat = 1; - ilac = 2; - ici = 3; + /* Solicited response: +CREG: <n>,<stat> */ + istat = 3; } else if (n_matches == 5) { + /* CREG=2 (GSM 07.07): +CREG: <stat>,<lac>,<ci> */ + istat = 2; + ilac = 3; + ici = 4; + } else if (n_matches == 6) { /* CREG=2 (ETSI 27.007): +CREG: <stat>,<lac>,<ci>,<AcT> * CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci> */ - /* 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: <n>,<stat>,<lac>,<ci>,<AcT> */ - 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/mm-serial-port.c b/src/mm-serial-port.c index a31e4299..8de0cdd1 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -321,8 +321,14 @@ real_config_fd (MMSerialPort *self, int fd, 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 (fd, TCSETA, &stbuf) < 0) { g_set_error (error, 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); |