aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-03-10 14:50:41 -0800
committerDan Williams <dcbw@redhat.com>2010-03-10 14:50:41 -0800
commit1979512d8dfb6428353e6bf358f908973a318095 (patch)
tree60f703f4a2c75c4e3a2d7d362969fd2c441ddb8c
parent8dde6bb8dd2c063f5740ae78b980343be8e5d669 (diff)
parentb7858ba235c046a514fbc79e18ac9faa75982032 (diff)
Merge remote branch 'origin/master' into qcdm
-rw-r--r--introspection/Makefile.am3
-rw-r--r--introspection/all.xml1
-rw-r--r--introspection/mm-modem-gsm-ussd.xml78
-rw-r--r--plugins/mm-modem-huawei-gsm.c39
-rw-r--r--plugins/mm-modem-mbm.c131
-rw-r--r--plugins/mm-modem-novatel-gsm.c16
-rw-r--r--plugins/mm-modem-zte.c37
-rw-r--r--src/main.c8
-rw-r--r--src/mm-generic-cdma.c3
-rw-r--r--src/mm-generic-gsm.c358
-rw-r--r--src/mm-generic-gsm.h5
-rw-r--r--src/mm-modem-helpers.c67
-rw-r--r--src/mm-modem-helpers.h2
-rw-r--r--src/mm-serial-port.c10
-rw-r--r--src/tests/test-modem-helpers.c22
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 */
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 <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);