Rework unsolicited message handling.

Implement registration using unsolicited messages for generic GSM class (which
is disabled by default, HSO and Huawei plugins enable it).

Modify all GSM modem subclasses that used unsolicited messages to use the new
method.
This commit is contained in:
Tambet Ingo
2009-02-18 14:48:08 +02:00
parent 13facad4fa
commit 1215bd6a9d
10 changed files with 379 additions and 535 deletions
+1 -3
View File
@@ -38,9 +38,7 @@ modem_manager_SOURCES = \
mm-serial.c \
mm-serial.h \
mm-serial-parsers.c \
mm-serial-parsers.h \
mm-util.c \
mm-util.h
mm-serial-parsers.h
mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml
dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $<
+172 -95
View File
@@ -22,11 +22,16 @@ typedef struct {
char *oper_name;
guint32 modem_type;
guint32 ip_method;
gboolean unsolicited_registration;
MMModemGsmNetworkRegStatus reg_status;
guint pending_registration;
guint32 signal_quality;
guint32 cid;
} MMGenericGsmPrivate;
static void pending_registration_stop (MMGenericGsm *self);
static void get_registration_status (MMSerial *serial, MMCallbackInfo *info);
static void read_operator_done (MMSerial *serial,
GString *response,
@@ -46,6 +51,15 @@ mm_generic_gsm_new (const char *serial_device, const char *driver)
NULL));
}
void
mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
gboolean enabled)
{
g_return_if_fail (MM_IS_GENERIC_GSM (modem));
MM_GENERIC_GSM_GET_PRIVATE (modem)->unsolicited_registration = enabled;
}
void
mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid)
{
@@ -62,6 +76,14 @@ 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)
{
}
void
mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
MMModemGsmNetworkRegStatus status)
@@ -75,11 +97,13 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
if (priv->reg_status != status) {
priv->reg_status = status;
g_debug ("Registration state changed: %d", 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);
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);
@@ -162,8 +186,11 @@ init_done (MMSerial *serial,
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else {
/* Disable unsolicited registration state changes, these will mess up our response parser */
mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL);
if (MM_GENERIC_GSM_GET_PRIVATE (serial)->unsolicited_registration)
mm_serial_queue_command (serial, "+CREG=1", 5, NULL, NULL);
else
mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL);
mm_serial_queue_command (serial, "+CFUN=1", 5, enable_done, user_data);
}
}
@@ -204,6 +231,8 @@ enable (MMModem *modem,
info = mm_callback_info_new (modem, callback, user_data);
if (!do_enable) {
pending_registration_stop (MM_GENERIC_GSM (modem));
if (mm_serial_is_connected (MM_SERIAL (modem)))
mm_serial_flash (MM_SERIAL (modem), 1000, disable_flash_done, info);
else
@@ -496,32 +525,124 @@ read_operator_done (MMSerial *serial,
}
}
/* Registration */
static gboolean
pending_registration_timed_out (gpointer data)
{
MMCallbackInfo *info = (MMCallbackInfo *) data;
MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration = 0;
return FALSE;
}
static void
pending_registration_stop (MMGenericGsm *self)
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
if (priv->pending_registration) {
g_source_remove (priv->pending_registration);
priv->pending_registration = 0;
}
}
static void
pending_registration_done (gpointer data)
{
MMCallbackInfo *info = (MMCallbackInfo *) data;
if (!info->error) {
switch (MM_GENERIC_GSM_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;
}
}
mm_callback_info_schedule (info);
}
static void
reg_status_updated (MMGenericGsm *self, int new_value)
{
MMModemGsmNetworkRegStatus status;
switch (new_value) {
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;
}
mm_generic_gsm_set_reg_status (self, status);
/* Stop the pending registration in case of success or certain failure */
if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING ||
status == MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED)
pending_registration_stop (self);
}
static void
reg_state_changed (MMSerial *serial,
GMatchInfo *match_info,
gpointer user_data)
{
char *str;
str = g_match_info_fetch (match_info, 1);
reg_status_updated (MM_GENERIC_GSM (serial), atoi (str));
g_free (str);
}
static gboolean
reg_status_again (gpointer data)
{
MMCallbackInfo *info = (MMCallbackInfo *) data;
guint32 counter;
counter = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "reg-status-counter"));
if (counter > 60) {
/* That's 60 seconds */
info->error = g_error_new_literal (MM_MOBILE_ERROR,
MM_MOBILE_ERROR_NETWORK_TIMEOUT,
"Registration timed out");
mm_callback_info_schedule (info);
} else {
mm_callback_info_set_data (info, "reg-status-counter",
GUINT_TO_POINTER (++counter), NULL);
if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration)
get_registration_status (MM_SERIAL (info->modem), info);
}
return TRUE;
return FALSE;
}
static void
reg_status_remove (gpointer data)
reg_status_again_remove (gpointer data)
{
g_source_remove (GPOINTER_TO_UINT (data));
g_source_remove (GPOINTER_TO_INT (data));
}
static void
@@ -531,90 +652,37 @@ get_reg_status_done (MMSerial *serial,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
const char *reply = response->str;
guint32 id;
gboolean done = FALSE;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial);
if (error) {
info->error = g_error_copy (error);
goto out;
pending_registration_done (info);
return;
}
if (g_str_has_prefix (reply, "+CREG: ")) {
if (g_str_has_prefix (response->str, "+CREG: ")) {
/* Got valid reply */
int n, stat;
int unsolicited, stat;
if (sscanf (reply + 7, "%d,%d", &n, &stat)) {
MMModemGsmNetworkRegStatus status;
if (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat)) {
reg_status_updated (MM_GENERIC_GSM (serial), stat);
switch (stat) {
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;
}
mm_generic_gsm_set_reg_status (MM_GENERIC_GSM (serial), status);
switch (status) {
case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
/* Done */
done = TRUE;
break;
case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE:
/* Huh? Stupid card, we told it to register, pretend it returned SEARCHING
(hoping it will eventually start searching) */
/* fall through */
case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING:
/* Wait more until the timeout expires. */
id = GPOINTER_TO_INT (mm_callback_info_get_data (info, "reg-status-timeout"));
if (!id) {
id = g_timeout_add (1000, reg_status_again, info);
mm_callback_info_set_data (info, "reg-status-timeout", GUINT_TO_POINTER (id),
reg_status_remove);
}
break;
case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED:
info->error = g_error_new_literal (MM_MOBILE_ERROR,
MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED,
"Network no allowed");
break;
case MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN:
default:
info->error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"Unknown network status");
break;
if (!unsolicited && priv->pending_registration) {
guint id;
id = g_timeout_add_seconds (1, reg_status_again, info);
mm_callback_info_set_data (info, "reg-status-again",
GINT_TO_POINTER (id),
reg_status_again_remove);
}
}
} else {
g_debug ("unknown response: %s", reply);
g_debug ("unknown response: %s", response->str);
info->error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"Could not parse the response");
pending_registration_done (info);
}
out:
if (done || info->error)
mm_callback_info_schedule (info);
}
static void
@@ -629,13 +697,8 @@ register_done (MMSerial *serial,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else
get_registration_status (serial, info);
/* Ignore errors here, get the actual registration status */
get_registration_status (serial, (MMCallbackInfo *) user_data);
}
static void
@@ -649,12 +712,18 @@ do_register (MMModemGsmNetwork *modem,
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_registration =
g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 60,
pending_registration_timed_out,
info,
pending_registration_done);
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, 60, register_done, info);
mm_serial_queue_command (MM_SERIAL (modem), command, 5, register_done, info);
g_free (command);
}
@@ -1366,10 +1435,16 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_gsm_init (MMGenericGsm *self)
{
GRegex *regex;
mm_serial_set_response_parser (MM_SERIAL (self),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_new (),
mm_serial_parser_v1_destroy);
regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, reg_state_changed, NULL, NULL);
g_regex_unref (regex);
}
static void
@@ -1432,6 +1507,8 @@ finalize (GObject *object)
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object);
pending_registration_stop (MM_GENERIC_GSM (object));
g_free (priv->driver);
g_free (priv->data_device);
g_free (priv->oper_code);
+3
View File
@@ -26,6 +26,9 @@ GType mm_generic_gsm_get_type (void);
MMModem *mm_generic_gsm_new (const char *serial_device,
const char *driver);
void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
gboolean enabled);
void mm_generic_gsm_set_cid (MMGenericGsm *modem,
guint32 cid);
+97
View File
@@ -49,6 +49,7 @@ typedef struct {
MMSerialResponseParserFn response_parser_fn;
gpointer response_parser_user_data;
GDestroyNotify response_parser_notify;
GSList *unsolicited_msg_handlers;
struct termios old_t;
@@ -65,6 +66,13 @@ typedef struct {
guint timeout_id;
} MMSerialPrivate;
typedef struct {
GRegex *regex;
MMSerialUnsolicitedMsgFn callback;
gpointer user_data;
GDestroyNotify notify;
} MMUnsolicitedMsgHandler;
const char *
mm_serial_get_device (MMSerial *serial)
{
@@ -435,6 +443,29 @@ mm_serial_queue_process (gpointer data)
return FALSE;
}
void
mm_serial_add_unsolicited_msg_handler (MMSerial *self,
GRegex *regex,
MMSerialUnsolicitedMsgFn callback,
gpointer user_data,
GDestroyNotify notify)
{
MMUnsolicitedMsgHandler *handler;
MMSerialPrivate *priv;
g_return_if_fail (MM_IS_SERIAL (self));
g_return_if_fail (regex != NULL);
handler = g_slice_new (MMUnsolicitedMsgHandler);
handler->regex = g_regex_ref (regex);
handler->callback = callback;
handler->user_data = user_data;
handler->notify = notify;
priv = MM_SERIAL_GET_PRIVATE (self);
priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
}
void
mm_serial_set_response_parser (MMSerial *self,
MMSerialResponseParserFn fn,
@@ -453,6 +484,58 @@ mm_serial_set_response_parser (MMSerial *self,
priv->response_parser_notify = notify;
}
static gboolean
remove_eval_cb (const GMatchInfo *match_info,
GString *result,
gpointer user_data)
{
int *result_len = (int *) user_data;
int start;
int end;
if (g_match_info_fetch_pos (match_info, 0, &start, &end))
*result_len -= (end - start);
return FALSE;
}
static void
parse_unsolicited_messages (MMSerial *self,
GString *response)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data;
GMatchInfo *match_info;
gboolean matches;
matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL);
if (handler->callback) {
while (g_match_info_matches (match_info)) {
handler->callback (self, match_info, handler->user_data);
g_match_info_next (match_info, NULL);
}
}
g_match_info_free (match_info);
if (matches) {
/* Remove matches */
char *str;
int result_len = response->len;
str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0,
remove_eval_cb, &result_len, NULL);
g_string_truncate (response, 0);
g_string_append_len (response, str, result_len);
g_free (str);
}
}
}
static gboolean
parse_response (MMSerial *self,
GString *response,
@@ -462,6 +545,8 @@ parse_response (MMSerial *self,
g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
parse_unsolicited_messages (self, response);
return priv->response_parser_fn (priv->response_parser_user_data, response, error);
}
@@ -841,6 +926,18 @@ finalize (GObject *object)
g_string_free (priv->response, TRUE);
g_free (priv->device);
while (priv->unsolicited_msg_handlers) {
MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
if (handler->notify)
handler->notify (handler->user_data);
g_regex_unref (handler->regex);
g_slice_free (MMUnsolicitedMsgHandler, handler);
priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
priv->unsolicited_msg_handlers);
}
if (priv->response_parser_notify)
priv->response_parser_notify (priv->response_parser_user_data);
+11
View File
@@ -3,6 +3,7 @@
#ifndef MM_SERIAL_H
#define MM_SERIAL_H
#include <glib.h>
#include <glib/gtypes.h>
#include <glib-object.h>
@@ -28,6 +29,10 @@ typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
GString *response,
GError **error);
typedef void (*MMSerialUnsolicitedMsgFn) (MMSerial *serial,
GMatchInfo *match_info,
gpointer user_data);
typedef void (*MMSerialResponseFn) (MMSerial *serial,
GString *response,
GError *error,
@@ -46,6 +51,12 @@ struct _MMSerialClass {
GType mm_serial_get_type (void);
void mm_serial_add_unsolicited_msg_handler (MMSerial *self,
GRegex *regex,
MMSerialUnsolicitedMsgFn callback,
gpointer user_data,
GDestroyNotify notify);
void mm_serial_set_response_parser (MMSerial *self,
MMSerialResponseParserFn fn,
gpointer user_data,
-57
View File
@@ -1,57 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "mm-util.h"
static gboolean
remove_eval_cb (const GMatchInfo *match_info,
GString *result,
gpointer user_data)
{
int *result_len = (int *) user_data;
int start;
int end;
if (g_match_info_fetch_pos (match_info, 0, &start, &end))
*result_len -= (end - start);
return FALSE;
}
void
mm_util_strip_string (GString *string,
GRegex *regex,
MMUtilStripFn callback,
gpointer user_data)
{
GMatchInfo *match_info;
gboolean matches;
char *str;
g_return_if_fail (string != NULL);
g_return_if_fail (regex != NULL);
matches = g_regex_match_full (regex, string->str, string->len, 0, 0, &match_info, NULL);
if (callback) {
while (g_match_info_matches (match_info)) {
str = g_match_info_fetch (match_info, 1);
callback (str, user_data);
g_free (str);
g_match_info_next (match_info, NULL);
}
}
g_match_info_free (match_info);
if (matches) {
/* Remove matches */
int result_len = string->len;
str = g_regex_replace_eval (regex, string->str, string->len, 0, 0,
remove_eval_cb, &result_len, NULL);
g_string_truncate (string, 0);
g_string_append_len (string, str, result_len);
g_free (str);
}
}
-20
View File
@@ -1,20 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef MM_UTIL_H
#define MM_UTIL_H
#include <glib.h>
typedef void (*MMUtilStripFn) (const char *str,
gpointer user_data);
/* Applies the regexp on string and calls the callback (if provided)
with each match and user_data. After that, the matches are removed
from the string.
*/
void mm_util_strip_string (GString *string,
GRegex *regex,
MMUtilStripFn callback,
gpointer user_data);
#endif /* MM_UTIL_H */