Implement Huawei modem registration with unsolicited messages (no polling).
Keep registration info in sync correctly. Emit signal when it changes.
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
|
||||
static gpointer mm_modem_huawei_parent_class = NULL;
|
||||
|
||||
static void pending_registration_stop (MMModemHuawei *self);
|
||||
|
||||
#define MM_MODEM_HUAWEI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI, MMModemHuaweiPrivate))
|
||||
|
||||
typedef struct {
|
||||
@@ -24,7 +26,11 @@ typedef struct {
|
||||
GRegex *status_regex;
|
||||
GRegex *reg_state_regex;
|
||||
|
||||
/* Pending operations */
|
||||
guint pending_registration;
|
||||
|
||||
/* Cached state */
|
||||
MMModemGsmNetworkRegStatus reg_status;
|
||||
guint signal_quality;
|
||||
MMModemGsmNetworkMode mode;
|
||||
MMModemGsmNetworkBand band;
|
||||
@@ -46,6 +52,63 @@ mm_modem_huawei_new (const char *data_device,
|
||||
NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
reg_status_updated (MMModemHuawei *self, int new_status)
|
||||
{
|
||||
MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (self);
|
||||
|
||||
switch (new_status) {
|
||||
case 0:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
|
||||
break;
|
||||
case 1:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME;
|
||||
break;
|
||||
case 2:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING;
|
||||
break;
|
||||
case 3:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED;
|
||||
break;
|
||||
case 4:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
|
||||
break;
|
||||
case 5:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
|
||||
break;
|
||||
default:
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop the pending registration in case of success or certain failure */
|
||||
if (priv->reg_status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
|
||||
priv->reg_status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING ||
|
||||
priv->reg_status == MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED)
|
||||
|
||||
pending_registration_stop (self);
|
||||
|
||||
g_debug ("Registration state changed: %d\n", priv->reg_status);
|
||||
mm_generic_gsm_set_reg_status (MM_GENERIC_GSM (self), priv->reg_status);
|
||||
}
|
||||
|
||||
static void
|
||||
got_reg_status (MMSerial *serial,
|
||||
GString *response,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (error)
|
||||
g_warning ("Error getting registration status: %s", error->message);
|
||||
else if (g_str_has_prefix (response->str, "+CREG: ")) {
|
||||
/* Got valid reply */
|
||||
int n, stat;
|
||||
|
||||
if (sscanf (response->str + 7, "%d,%d", &n, &stat))
|
||||
reg_status_updated (MM_MODEM_HUAWEI (serial), stat);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parent_enable_done (MMModem *modem, GError *error, gpointer user_data)
|
||||
{
|
||||
@@ -53,9 +116,11 @@ parent_enable_done (MMModem *modem, GError *error, gpointer user_data)
|
||||
|
||||
if (error)
|
||||
info->error = g_error_copy (error);
|
||||
else
|
||||
/* Enable unsolicited registration state changes */
|
||||
else {
|
||||
/* Enable unsolicited registration state changes and get the current state */
|
||||
mm_serial_queue_command (MM_SERIAL (modem), "+CREG=1", 5, NULL, NULL);
|
||||
mm_serial_queue_command (MM_SERIAL (modem), "+CREG?", 5, got_reg_status, NULL);
|
||||
}
|
||||
|
||||
mm_callback_info_schedule (info);
|
||||
}
|
||||
@@ -75,9 +140,106 @@ enable (MMModem *modem,
|
||||
|
||||
info = mm_callback_info_new (modem, callback, user_data);
|
||||
parent_modem_iface->enable (modem, enable, parent_enable_done, info);
|
||||
} else
|
||||
} else {
|
||||
pending_registration_stop (MM_MODEM_HUAWEI (modem));
|
||||
parent_modem_iface->enable (modem, enable, callback, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pending_registration_set (MMModemHuawei *self, guint tag)
|
||||
{
|
||||
MM_MODEM_HUAWEI_GET_PRIVATE (self)->pending_registration = tag;
|
||||
}
|
||||
|
||||
static void
|
||||
pending_registration_stop (MMModemHuawei *self)
|
||||
{
|
||||
guint tag;
|
||||
|
||||
tag = MM_MODEM_HUAWEI_GET_PRIVATE (self)->pending_registration;
|
||||
if (tag)
|
||||
g_source_remove (tag);
|
||||
}
|
||||
|
||||
static void
|
||||
pending_registration_cleanup (gpointer data)
|
||||
{
|
||||
MMCallbackInfo *info = (MMCallbackInfo *) data;
|
||||
|
||||
if (!info->error) {
|
||||
switch (MM_MODEM_HUAWEI_GET_PRIVATE (info->modem)->reg_status) {
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
|
||||
/* Successfully registered */
|
||||
break;
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED:
|
||||
info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED);
|
||||
break;
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING:
|
||||
info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
|
||||
break;
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE:
|
||||
info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK);
|
||||
break;
|
||||
default:
|
||||
info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pending_registration_set (MM_MODEM_HUAWEI (info->modem), 0);
|
||||
mm_callback_info_schedule (info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pending_registration_timed_out (gpointer data)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
register_done (MMSerial *serial,
|
||||
GString *response,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
|
||||
|
||||
if (error) {
|
||||
info->error = g_error_copy (error);
|
||||
mm_callback_info_schedule (info);
|
||||
} else {
|
||||
/* Add a timeout to wait for the unsolicited "connected" message */
|
||||
pending_registration_set (MM_MODEM_HUAWEI (serial),
|
||||
g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 60,
|
||||
pending_registration_timed_out,
|
||||
info,
|
||||
pending_registration_cleanup));
|
||||
|
||||
mm_serial_queue_command (serial, "+CREG?", 5, got_reg_status, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_register (MMModemGsmNetwork *modem,
|
||||
const char *network_id,
|
||||
MMModemFn callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
MMCallbackInfo *info;
|
||||
char *command;
|
||||
|
||||
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
|
||||
|
||||
if (network_id)
|
||||
command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id);
|
||||
else
|
||||
command = g_strdup ("+COPS=0,,");
|
||||
|
||||
mm_serial_queue_command (MM_SERIAL (modem), command, 5, register_done, info);
|
||||
g_free (command);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_syscfg (MMModemHuawei *self,
|
||||
@@ -465,36 +627,7 @@ handle_status_change (const char *str, gpointer data)
|
||||
static void
|
||||
reg_state_changed (const char *str, gpointer data)
|
||||
{
|
||||
int i;
|
||||
MMModemGsmNetworkRegStatus status;
|
||||
|
||||
i = atoi (str);
|
||||
switch (i) {
|
||||
case 0:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
|
||||
break;
|
||||
case 1:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME;
|
||||
break;
|
||||
case 2:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING;
|
||||
break;
|
||||
case 3:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED;
|
||||
break;
|
||||
case 4:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
|
||||
break;
|
||||
case 5:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
|
||||
break;
|
||||
default:
|
||||
status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
g_debug ("Registration state changed: %d\n", status);
|
||||
mm_generic_gsm_set_reg_status (MM_GENERIC_GSM (data), status);
|
||||
reg_status_updated (MM_MODEM_HUAWEI (data), atoi (str));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -521,6 +654,7 @@ modem_init (MMModem *modem_class)
|
||||
static void
|
||||
modem_gsm_network_init (MMModemGsmNetwork *class)
|
||||
{
|
||||
class->do_register = do_register;
|
||||
class->set_network_mode = set_network_mode;
|
||||
class->get_network_mode = get_network_mode;
|
||||
class->set_band = set_band;
|
||||
@@ -533,6 +667,8 @@ mm_modem_huawei_init (MMModemHuawei *self)
|
||||
{
|
||||
MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (self);
|
||||
|
||||
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
|
||||
|
||||
priv->signal_quality_regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
||||
priv->mode_regex = g_regex_new ("\\r\\n\\^MODE:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
||||
priv->status_regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
|
||||
@@ -547,6 +683,8 @@ finalize (GObject *object)
|
||||
{
|
||||
MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object);
|
||||
|
||||
pending_registration_stop (MM_MODEM_HUAWEI (object));
|
||||
|
||||
mm_serial_parser_v1_destroy (priv->std_parser);
|
||||
g_regex_unref (priv->signal_quality_regex);
|
||||
g_regex_unref (priv->mode_regex);
|
||||
|
@@ -25,8 +25,9 @@ typedef struct {
|
||||
} MMGenericGsmPrivate;
|
||||
|
||||
static void get_registration_status (MMSerial *serial, MMCallbackInfo *info);
|
||||
static void get_signal_quality (MMModemGsmNetwork *modem,
|
||||
MMModemUIntFn callback,
|
||||
static void read_operator_done (MMSerial *serial,
|
||||
GString *response,
|
||||
GError *error,
|
||||
gpointer user_data);
|
||||
|
||||
MMModem *
|
||||
@@ -60,16 +61,6 @@ mm_generic_gsm_get_cid (MMGenericGsm *modem)
|
||||
void
|
||||
mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
|
||||
MMModemGsmNetworkRegStatus status)
|
||||
{
|
||||
g_return_if_fail (MM_IS_GENERIC_GSM (modem));
|
||||
|
||||
MM_GENERIC_GSM_GET_PRIVATE (modem)->reg_status = status;
|
||||
}
|
||||
|
||||
void
|
||||
mm_generic_gsm_set_operator (MMGenericGsm *modem,
|
||||
const char *code,
|
||||
const char *name)
|
||||
{
|
||||
MMGenericGsmPrivate *priv;
|
||||
|
||||
@@ -77,11 +68,23 @@ mm_generic_gsm_set_operator (MMGenericGsm *modem,
|
||||
|
||||
priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
|
||||
|
||||
if (priv->reg_status != status) {
|
||||
priv->reg_status = status;
|
||||
|
||||
if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
|
||||
status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
|
||||
mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,2;+COPS?", 3, read_operator_done, GINT_TO_POINTER (0));
|
||||
mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,0;+COPS?", 3, read_operator_done, GINT_TO_POINTER (1));
|
||||
mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), NULL, NULL);
|
||||
} else {
|
||||
g_free (priv->oper_code);
|
||||
g_free (priv->oper_name);
|
||||
priv->oper_code = priv->oper_name = NULL;
|
||||
|
||||
priv->oper_code = g_strdup (code);
|
||||
priv->oper_name = g_strdup (name);
|
||||
mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (modem), priv->reg_status,
|
||||
priv->oper_code, priv->oper_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -460,43 +463,28 @@ parse_operator (const char *reply)
|
||||
}
|
||||
|
||||
static void
|
||||
get_reg_code_done (MMSerial *serial,
|
||||
read_operator_done (MMSerial *serial,
|
||||
GString *response,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
const char *reply = response->str;
|
||||
|
||||
if (!error) {
|
||||
char *oper;
|
||||
|
||||
oper = parse_operator (reply);
|
||||
oper = parse_operator (response->str);
|
||||
if (oper) {
|
||||
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial);
|
||||
|
||||
if (GPOINTER_TO_INT (user_data) == 0) {
|
||||
g_free (priv->oper_code);
|
||||
priv->oper_code = oper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_reg_name_done (MMSerial *serial,
|
||||
GString *response,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
const char *reply = response->str;
|
||||
|
||||
if (!error) {
|
||||
char *oper;
|
||||
|
||||
oper = parse_operator (reply);
|
||||
if (oper) {
|
||||
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial);
|
||||
|
||||
} else {
|
||||
g_free (priv->oper_name);
|
||||
priv->oper_name = oper;
|
||||
|
||||
mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (serial), priv->reg_status,
|
||||
priv->oper_code, priv->oper_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -582,9 +570,6 @@ get_reg_status_done (MMSerial *serial,
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
|
||||
/* Done */
|
||||
mm_serial_queue_command (serial, "+COPS=3,2;+COPS?", 3, get_reg_code_done, NULL);
|
||||
mm_serial_queue_command (serial, "+COPS=3,0;+COPS?", 3, get_reg_name_done, NULL);
|
||||
get_signal_quality (MM_MODEM_GSM_NETWORK (serial), NULL, NULL);
|
||||
done = TRUE;
|
||||
break;
|
||||
case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE:
|
||||
|
@@ -33,10 +33,6 @@ guint32 mm_generic_gsm_get_cid (MMGenericGsm *modem);
|
||||
void mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
|
||||
MMModemGsmNetworkRegStatus status);
|
||||
|
||||
void mm_generic_gsm_set_operator (MMGenericGsm *modem,
|
||||
const char *code,
|
||||
const char *name);
|
||||
|
||||
void mm_generic_gsm_check_pin (MMGenericGsm *modem,
|
||||
MMModemFn callback,
|
||||
gpointer user_data);
|
||||
|
Reference in New Issue
Block a user