Merge remote branch 'origin/master' into qcdm

This commit is contained in:
Dan Williams
2010-03-10 14:50:41 -08:00
15 changed files with 543 additions and 233 deletions

View File

@@ -14,4 +14,5 @@ EXTRA_DIST = \
mm-modem-gsm-sms.xml \ mm-modem-gsm-sms.xml \
mm-modem-simple.xml \ mm-modem-simple.xml \
mm-serial-error.xml \ mm-serial-error.xml \
mm-modem-location.xml mm-modem-location.xml \
mm-modem-gsm-ussd.xml

View File

@@ -33,6 +33,7 @@
<xi:include href="mm-modem-gsm-network.xml"/> <xi:include href="mm-modem-gsm-network.xml"/>
<xi:include href="mm-modem-gsm-sms.xml"/> <xi:include href="mm-modem-gsm-sms.xml"/>
<xi:include href="mm-modem-gsm-hso.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-serial-error.xml"/>
<xi:include href="mm-modem-error.xml"/> <xi:include href="mm-modem-error.xml"/>

View File

@@ -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>

View File

@@ -40,7 +40,6 @@ G_DEFINE_TYPE_EXTENDED (MMModemHuaweiGsm, mm_modem_huawei_gsm, MM_TYPE_GENERIC_G
typedef struct { typedef struct {
/* Cached state */ /* Cached state */
guint signal_quality;
MMModemGsmBand band; MMModemGsmBand band;
} MMModemHuaweiGsmPrivate; } 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 */ /* Unsolicited message handlers */
static void static void
@@ -400,24 +376,22 @@ handle_signal_quality_change (MMAtSerialPort *port,
gpointer user_data) gpointer user_data)
{ {
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data); MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data);
MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
char *str; char *str;
int quality; int quality = 0;
str = g_match_info_fetch (match_info, 1); str = g_match_info_fetch (match_info, 1);
quality = atoi (str); quality = atoi (str);
g_free (str); g_free (str);
if (quality == 99) if (quality == 99) {
/* 99 means unknown */ /* 99 means unknown */
quality = 0; quality = 0;
else } else {
/* Normalize the quality */ /* Normalize the quality */
quality = quality * 100 / 31; quality = CLAMP (quality, 0, 31) * 100 / 31;
}
g_debug ("Signal quality: %d", quality); mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (self), (guint32) quality);
priv->signal_quality = (guint32) quality;
mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), (guint32) quality);
} }
static void static void
@@ -568,7 +542,6 @@ modem_gsm_network_init (MMModemGsmNetwork *class)
{ {
class->set_band = set_band; class->set_band = set_band;
class->get_band = get_band; class->get_band = get_band;
class->get_signal_quality = get_signal_quality;
} }
static void static void

View File

@@ -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_2G 5
#define MBM_NETWORK_MODE_3G 6 #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 { typedef struct {
guint reg_id; guint reg_id;
gboolean have_emrdy; 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); primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
g_assert (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 = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self));
parent_modem_iface->do_register (MM_MODEM_GSM_NETWORK (self), parent_modem_iface->do_register (MM_MODEM_GSM_NETWORK (self),
reg_data->network_id, reg_data->network_id,
@@ -214,57 +206,47 @@ set_allowed_mode (MMGenericGsm *gsm,
g_free (command); g_free (command);
} }
#if 0
static void static void
get_network_mode_done (MMAtSerialPort *port, mbm_erinfo_received (MMAtSerialPort *port,
GString *response, GMatchInfo *info,
GError *error, gpointer user_data)
gpointer user_data)
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
char *erinfo; char *str;
int mode = 0, gsm = 0, umts = 0;
gboolean parsed = FALSE;
if (error) { str = g_match_info_fetch (info, 2);
info->error = g_error_copy (error); if (str) {
goto done; 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:"); /* 3G modes take precedence */
if (!erinfo) str = g_match_info_fetch (info, 3);
goto done; if (str) {
switch (atoi (str)) {
if (sscanf (erinfo + 8, "%d,%d,%d", &mode, &gsm, &umts) != 3) case 1:
goto done; act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
break;
if (gsm || umts) { case 2:
MMModemGsmMode mm_mode = MM_MODEM_GSM_MODE_ANY; act = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
break;
if (gsm == MBM_ERINFO_2G_GPRS) default:
mm_mode = MM_MODEM_GSM_MODE_GPRS; break;
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;
} }
g_free (str);
done: mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
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);
} }
#endif
static void static void
get_allowed_mode_done (MMAtSerialPort *port, get_allowed_mode_done (MMAtSerialPort *port,
@@ -364,6 +346,10 @@ mbm_enable_done (MMAtSerialPort *port,
{ {
MMCallbackInfo *info = (MMCallbackInfo *) user_data; 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); mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
} }
@@ -456,10 +442,10 @@ typedef struct {
} DisableInfo; } DisableInfo;
static void static void
disable_creg_cmer_done (MMAtSerialPort *port, disable_unsolicited_done (MMAtSerialPort *port,
GString *response, GString *response,
GError *error, GError *error,
gpointer user_data) gpointer user_data)
{ {
MMModem *parent_modem_iface; 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); primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
g_assert (primary); g_assert (primary);
/* Turn off unsolicited +CIEV signal strength indicator */ /* Turn off unsolicited responses */
mm_at_serial_port_queue_command (primary, "+CREG=0;+CMER=0", 5, disable_creg_cmer_done, info); mm_at_serial_port_queue_command (primary, "+CMER=0;*ERINFO=0", 5, disable_unsolicited_done, info);
} }
static void static void
@@ -551,18 +537,20 @@ mbm_ciev_received (MMAtSerialPort *port,
gpointer user_data) gpointer user_data)
{ {
int quality = 0, ind = 0; int quality = 0, ind = 0;
const char *str; char *str;
str = g_match_info_fetch (info, 1); str = g_match_info_fetch (info, 1);
if (str) if (str)
ind = atoi (str); ind = atoi (str);
g_free (str);
if (ind == MBM_SIGNAL_INDICATOR) { if (ind == MBM_SIGNAL_INDICATOR) {
str = g_match_info_fetch (info, 2); str = g_match_info_fetch (info, 2);
if (str) { if (str) {
quality = atoi (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) gpointer user_data)
{ {
int state = 0; int state = 0;
const char *str; char *str;
str = g_match_info_fetch (info, 1); str = g_match_info_fetch (info, 1);
if (str) if (str)
state = atoi (str); state = atoi (str);
g_free (str);
if (MBM_E2NAP_DISCONNECTED == state) { if (MBM_E2NAP_DISCONNECTED == state) {
g_debug ("%s: disconnected", __func__); g_debug ("%s: disconnected", __func__);
@@ -791,17 +780,19 @@ grab_port (MMModem *modem,
} }
port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); 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; 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); 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); mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_emrdy_received, modem, NULL);
g_regex_unref (regex); 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); 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); mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL);
g_regex_unref (regex); 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 */ /* 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); 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); g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); 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); mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex); 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; return TRUE;

View File

@@ -48,13 +48,27 @@ mm_modem_novatel_gsm_new (const char *device,
/* Modem class override functions */ /* Modem class override functions */
/*****************************************************************************/ /*****************************************************************************/
static void
dmat_callback2 (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
mm_serial_port_close (MM_SERIAL_PORT (port));
}
static void static void
dmat_callback (MMAtSerialPort *port, dmat_callback (MMAtSerialPort *port,
GString *response, GString *response,
GError *error, GError *error,
gpointer user_data) 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 static gboolean

View File

@@ -52,6 +52,39 @@ mm_modem_zte_new (const char *device,
NULL)); 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 */ /* Modem class override functions */
/*****************************************************************************/ /*****************************************************************************/
@@ -223,8 +256,8 @@ grab_port (MMModem *modem,
g_regex_unref (regex); g_regex_unref (regex);
/* Current network and service domain */ /* Current network and service domain */
regex = g_regex_new ("\\r\\n\\+ZPASR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, 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, NULL, NULL, 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); g_regex_unref (regex);
/* SIM request to Build Main Menu */ /* SIM request to Build Main Menu */

View File

@@ -17,6 +17,7 @@
#include <signal.h> #include <signal.h>
#include <syslog.h> #include <syslog.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h> #include <dbus/dbus-glib-lowlevel.h>
#include "mm-manager.h" #include "mm-manager.h"
@@ -33,8 +34,11 @@ mm_signal_handler (int signo)
mm_options_set_debug (!mm_options_debug ()); mm_options_set_debug (!mm_options_debug ());
else if (signo == SIGINT || signo == SIGTERM) { else if (signo == SIGINT || signo == SIGTERM) {
g_message ("Caught signal %d, shutting down...", signo); 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 static void

View File

@@ -358,6 +358,9 @@ enable_all_done (MMModem *modem, GError *error, gpointer user_data)
if (error) if (error)
info->error = g_error_copy (error); info->error = g_error_copy (error);
else { 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 */ /* Open up the second port, if one exists */
if (priv->secondary) { if (priv->secondary) {
if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &info->error)) { if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &info->error)) {

View File

@@ -79,6 +79,8 @@ typedef struct {
guint pending_reg_id; guint pending_reg_id;
MMCallbackInfo *pending_reg_info; MMCallbackInfo *pending_reg_info;
guint signal_quality_id;
time_t signal_quality_timestamp;
guint32 signal_quality; guint32 signal_quality;
guint32 cid; guint32 cid;
@@ -113,6 +115,17 @@ static gboolean handle_reg_status_response (MMGenericGsm *self,
static MMModemGsmAccessTech etsi_act_to_mm_act (gint act); 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 * MMModem *
mm_generic_gsm_new (const char *device, mm_generic_gsm_new (const char *device,
const char *driver, const char *driver,
@@ -145,14 +158,6 @@ mm_generic_gsm_get_cid (MMGenericGsm *modem)
return MM_GENERIC_GSM_GET_PRIVATE (modem)->cid; return MM_GENERIC_GSM_GET_PRIVATE (modem)->cid;
} }
static void
got_signal_quality (MMModem *modem,
guint32 result,
GError *error,
gpointer user_data)
{
}
typedef struct { typedef struct {
const char *result; const char *result;
const char *normalized; 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 */ /* Get allowed mode */
if (MM_GENERIC_GSM_GET_CLASS (self)->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); 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); info->error = mm_modem_check_removed (info->modem, error);
if (!info->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_serial_port_close (MM_SERIAL_PORT (port));
mm_modem_set_state (MM_MODEM (info->modem), mm_modem_set_state (MM_MODEM (info->modem),
MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_REASON_NONE); 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); mm_callback_info_schedule (info);
} }
@@ -903,11 +916,16 @@ disable (MMModem *modem,
priv->poll_id = 0; 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[0] = 0;
priv->lac[1] = 0; priv->lac[1] = 0;
priv->cell_id[0] = 0; priv->cell_id[0] = 0;
priv->cell_id[1] = 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 */ /* Close the secondary port if its open */
if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
@@ -1268,6 +1286,49 @@ change_pin (MMModemGsmCard *modem,
g_free (command); 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 * static char *
parse_operator (const char *reply) parse_operator (const char *reply)
{ {
@@ -1300,18 +1361,14 @@ read_operator_code_done (MMAtSerialPort *port,
GError *error, GError *error,
gpointer user_data) gpointer user_data)
{ {
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); MMGenericGsm *self = MM_GENERIC_GSM (user_data);
char *oper; char *oper;
if (error) if (!error) {
return; oper = parse_operator (response->str);
if (oper)
oper = parse_operator (response->str); reg_info_updated (self, FALSE, 0, TRUE, oper, FALSE, NULL);
if (!oper) }
return;
g_free (priv->oper_code);
priv->oper_code = oper;
} }
static void static void
@@ -1320,23 +1377,14 @@ read_operator_name_done (MMAtSerialPort *port,
GError *error, GError *error,
gpointer user_data) gpointer user_data)
{ {
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); MMGenericGsm *self = MM_GENERIC_GSM (user_data);
char *oper; char *oper;
if (error) if (!error) {
return; oper = parse_operator (response->str);
if (oper)
oper = parse_operator (response->str); reg_info_updated (self, FALSE, 0, FALSE, NULL, TRUE, oper);
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);
} }
/* Registration */ /* 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 void
mm_generic_gsm_set_reg_status (MMGenericGsm *modem, mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
MMModemGsmNetworkRegStatus status) 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,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_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); mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL);
} else { } else
g_free (priv->oper_code); reg_info_updated (MM_GENERIC_GSM (modem), FALSE, 0, TRUE, NULL, TRUE, NULL);
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);
}
mm_generic_gsm_update_enabled_state (modem, TRUE, MM_MODEM_STATE_REASON_NONE); mm_generic_gsm_update_enabled_state (modem, TRUE, MM_MODEM_STATE_REASON_NONE);
} }
@@ -1480,14 +1531,21 @@ reg_state_changed (MMAtSerialPort *port,
return; return;
} }
if (reg_status_updated (self, state, NULL)) { /* Don't update reg state on CGREG responses since for many devices it's
/* If registration is finished (either registered or failed) but the * unclear what that registration state that actually reflects. We'll
* registration query hasn't completed yet, just remove the timeout and * take CGREG registration state into account later when we have a more
* let the registration query complete. * consistent way of handling it.
*/ */
if (priv->pending_reg_id) { if (cgreg == FALSE) {
g_source_remove (priv->pending_reg_id); if (reg_status_updated (self, state, NULL)) {
priv->pending_reg_id = 0; /* 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); 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); info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (info->modem)); mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (info->modem));
@@ -2133,26 +2195,91 @@ set_apn (MMModemGsmNetwork *modem,
/* GetSignalQuality */ /* 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 static void
get_signal_quality_done (MMAtSerialPort *port, get_signal_quality_done (MMAtSerialPort *port,
GString *response, GString *response,
GError *error, GError *error,
gpointer user_data) gpointer user_data)
{ {
MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char *reply = response->str; char *reply = response->str;
gboolean parsed = FALSE;
if (error) info->error = mm_modem_check_removed (info->modem, error);
info->error = g_error_copy (error); if (info->error)
else if (!strncmp (reply, "+CSQ: ", 6)) { goto done;
if (!strncmp (reply, "+CSQ: ", 6)) {
/* Got valid reply */ /* Got valid reply */
int quality; int quality;
int ber; int ber;
reply += 6; if (sscanf (reply + 6, "%d, %d", &quality, &ber)) {
if (sscanf (reply, "%d, %d", &quality, &ber)) {
/* 99 means unknown */ /* 99 means unknown */
if (quality == 99) { if (quality == 99) {
info->error = g_error_new_literal (MM_MOBILE_ERROR, info->error = g_error_new_literal (MM_MOBILE_ERROR,
@@ -2162,15 +2289,19 @@ get_signal_quality_done (MMAtSerialPort *port,
/* Normalize the quality */ /* Normalize the quality */
quality = CLAMP (quality, 0, 31) * 100 / 31; quality = CLAMP (quality, 0, 31) * 100 / 31;
priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (info->modem), quality);
priv->signal_quality = quality;
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
} }
} else parsed = TRUE;
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, }
"Could not parse signal quality results");
} }
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); mm_callback_info_schedule (info);
} }
@@ -2185,7 +2316,6 @@ get_signal_quality (MMModemGsmNetwork *modem,
connected = mm_port_get_connected (MM_PORT (priv->primary)); connected = mm_port_get_connected (MM_PORT (priv->primary));
if (connected && !priv->secondary) { if (connected && !priv->secondary) {
g_message ("Returning saved signal quality %d", priv->signal_quality);
callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data); callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data);
return; return;
} }
@@ -2221,19 +2351,19 @@ etsi_act_to_mm_act (gint act)
while (iter->mm_act != MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) { while (iter->mm_act != MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) {
if (iter->etsi_act == act) if (iter->etsi_act == act)
return iter->mm_act; return iter->mm_act;
iter++;
} }
return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
} }
void static void
mm_generic_gsm_update_access_technology (MMGenericGsm *modem, _internal_update_access_technology (MMGenericGsm *modem,
MMModemGsmAccessTech act) MMModemGsmAccessTech act)
{ {
MMGenericGsmPrivate *priv; MMGenericGsmPrivate *priv;
g_return_if_fail (modem != NULL); g_return_if_fail (modem != NULL);
g_return_if_fail (MM_IS_GENERIC_GSM (modem)); 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); 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); priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
@@ -2250,6 +2380,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 void
mm_generic_gsm_update_allowed_mode (MMGenericGsm *self, mm_generic_gsm_update_allowed_mode (MMGenericGsm *self,
MMModemGsmAllowedMode mode) MMModemGsmAllowedMode mode)
@@ -2509,8 +2651,6 @@ simple_connect (MMModemSimple *simple,
simple_state_machine (MM_MODEM (simple), NULL, info); simple_state_machine (MM_MODEM (simple), NULL, info);
} }
static void static void
simple_free_gvalue (gpointer data) simple_free_gvalue (gpointer data)
{ {
@@ -2542,16 +2682,39 @@ simple_string_value (const char *str)
return val; 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 static void
simple_status_got_signal_quality (MMModem *modem, simple_status_got_signal_quality (MMModem *modem,
guint32 result, guint32 result,
GError *error, GError *error,
gpointer user_data) gpointer user_data)
{ {
if (error) MMCallbackInfo *info = (MMCallbackInfo *) user_data;
g_warning ("Error getting signal quality: %s", error->message); GHashTable *properties;
else
g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result)); 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 static void
@@ -2560,9 +2723,14 @@ simple_status_got_band (MMModem *modem,
GError *error, GError *error,
gpointer user_data) gpointer user_data)
{ {
/* Ignore band errors since there's no generic implementation for it */ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (!error) GHashTable *properties;
g_hash_table_insert ((GHashTable *) user_data, "band", simple_uint_value (result));
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 static void
@@ -2576,17 +2744,15 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem,
MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMCallbackInfo *info = (MMCallbackInfo *) user_data;
GHashTable *properties; GHashTable *properties;
if (error) info->error = mm_modem_check_removed ((MMModem *) modem, error);
info->error = g_error_copy (error); if (!info->error) {
else { properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
properties = (GHashTable *) mm_callback_info_get_data (info, "simple-get-status");
g_hash_table_insert (properties, "registration_status", simple_uint_value (status)); 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_code", simple_string_value (oper_code));
g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name)); g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name));
} }
simple_status_complete_item (info);
mm_callback_info_schedule (info);
} }
static void static void
@@ -2595,7 +2761,7 @@ simple_get_status_invoke (MMCallbackInfo *info)
MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback; MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback;
callback (MM_MODEM_SIMPLE (info->modem), 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); info->error, info->user_data);
} }
@@ -2615,12 +2781,15 @@ simple_get_status (MMModemSimple *simple,
G_CALLBACK (callback), G_CALLBACK (callback),
user_data); user_data);
properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue); properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue);
mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref); 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_signal_quality (gsm, simple_status_got_signal_quality, info);
mm_modem_gsm_network_get_band (gsm, simple_status_got_band, properties); 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, properties); 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) { if (priv->act > -1) {
/* Deprecated key */ /* Deprecated key */
@@ -2791,6 +2960,11 @@ finalize (GObject *object)
priv->poll_id = 0; 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); mm_gsm_creg_regex_destroy (priv->reg_regex);
g_free (priv->oper_code); g_free (priv->oper_code);

View File

@@ -133,6 +133,11 @@ void mm_generic_gsm_update_allowed_mode (MMGenericGsm *modem,
void mm_generic_gsm_update_access_technology (MMGenericGsm *modem, void mm_generic_gsm_update_access_technology (MMGenericGsm *modem,
MMModemGsmAccessTech act); 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, void mm_generic_gsm_check_pin (MMGenericGsm *modem,
MMModemFn callback, MMModemFn callback,
gpointer user_data); gpointer user_data);

View File

@@ -205,22 +205,22 @@ mm_gsm_destroy_scan_data (gpointer data)
/*************************************************************************/ /*************************************************************************/
/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */ /* +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) */ /* +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) */ /* +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) */ /* +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) */ /* +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) */ /* +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 * GPtrArray *
mm_gsm_creg_regex_get (gboolean solicited) mm_gsm_creg_regex_get (gboolean solicited)
@@ -319,7 +319,7 @@ mm_gsm_parse_creg_response (GMatchInfo *info,
gulong *out_lac, gulong *out_lac,
gulong *out_ci, gulong *out_ci,
gint *out_act, gint *out_act,
gboolean *out_greg, gboolean *out_cgreg,
GError **error) GError **error)
{ {
gboolean success = FALSE, foo; gboolean success = FALSE, foo;
@@ -327,56 +327,60 @@ mm_gsm_parse_creg_response (GMatchInfo *info,
gulong stat = 0, lac = 0, ci = 0; gulong stat = 0, lac = 0, ci = 0;
guint istat = 0, ilac = 0, ici = 0, iact = 0; guint istat = 0, ilac = 0, ici = 0, iact = 0;
char *str; char *str;
const char *orig_str;
g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (out_reg_state != 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_lac != NULL, FALSE);
g_return_val_if_fail (out_ci != 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_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 /* Normally the number of matches could be used to determine what each
* item is, but we have overlap in one case. * item is, but we have overlap in one case.
*/ */
n_matches = g_match_info_get_match_count (info); n_matches = g_match_info_get_match_count (info);
if (n_matches == 2) { if (n_matches == 3) {
/* CREG=1: +CREG: <stat> */ /* CREG=1: +CREG: <stat> */
istat = 1;
} else if (n_matches == 3) {
/* Solicited response: +CREG: <n>,<stat> */
istat = 2; istat = 2;
} else if (n_matches == 4) { } else if (n_matches == 4) {
/* CREG=2 (GSM 07.07): +CREG: <stat>,<lac>,<ci> */ /* Solicited response: +CREG: <n>,<stat> */
istat = 1; istat = 3;
ilac = 2;
ici = 3;
} else if (n_matches == 5) { } 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 (ETSI 27.007): +CREG: <stat>,<lac>,<ci>,<AcT>
* CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci> * CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>
*/ */
/* To distinguish, check length of the second match item. If it's /* To distinguish, check length of the third match item. If it's
* more than one digit or has quotes in it, then we have the first format. * 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)) { if (str && (strchr (str, '"') || strlen (str) > 1)) {
g_free (str); g_free (str);
istat = 1;
ilac = 2;
ici = 3;
iact = 4;
} else {
istat = 2; istat = 2;
ilac = 3; ilac = 3;
ici = 4; 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> */ /* CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>,<AcT> */
istat = 2; istat = 3;
ilac = 3; ilac = 4;
ici = 4; ici = 5;
iact = 5; iact = 6;
} }
/* Status */ /* Status */
@@ -416,9 +420,6 @@ mm_gsm_parse_creg_response (GMatchInfo *info,
act = -1; act = -1;
} }
orig_str = g_match_info_get_string (info);
*out_greg = !!strstr (orig_str, "+CGREG");
*out_reg_state = (guint32) stat; *out_reg_state = (guint32) stat;
if (stat != 4) { if (stat != 4) {
/* Don't fill in lac/ci/act if the device's state is unknown */ /* Don't fill in lac/ci/act if the device's state is unknown */

View File

@@ -36,7 +36,7 @@ gboolean mm_gsm_parse_creg_response (GMatchInfo *info,
gulong *out_lac, gulong *out_lac,
gulong *out_ci, gulong *out_ci,
gint *out_act, gint *out_act,
gboolean *out_greg, gboolean *out_cgreg,
GError **error); GError **error);
#endif /* MM_MODEM_HELPERS_H */ #endif /* MM_MODEM_HELPERS_H */

View File

@@ -321,8 +321,14 @@ real_config_fd (MMSerialPort *self, int fd, GError **error)
stbuf.c_cc[VTIME] = 0; stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1; stbuf.c_cc[VEOF] = 1;
stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB); /* Use software handshaking */
stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits); 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) { if (ioctl (fd, TCSETA, &stbuf) < 0) {
g_set_error (error, g_set_error (error,

View File

@@ -670,6 +670,25 @@ test_cgreg2_f3607gw_unsolicited (void *f, gpointer d)
test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result); 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 * static TestData *
test_data_new (void) 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_solicited, data));
g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_unsolicited, 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 (); result = g_test_run ();
test_data_free (data); test_data_free (data);