diff options
-rw-r--r-- | plugins/mm-modem-hso.c | 46 | ||||
-rw-r--r-- | plugins/mm-modem-huawei.c | 342 | ||||
-rw-r--r-- | plugins/mm-modem-mbm.c | 71 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 273 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 3 | ||||
-rw-r--r-- | src/mm-serial.c | 97 | ||||
-rw-r--r-- | src/mm-serial.h | 11 | ||||
-rw-r--r-- | src/mm-util.c | 57 | ||||
-rw-r--r-- | src/mm-util.h | 20 |
10 files changed, 384 insertions, 540 deletions
diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c index 9c9d7d20..26b986cd 100644 --- a/plugins/mm-modem-hso.c +++ b/plugins/mm-modem-hso.c @@ -12,7 +12,6 @@ #include "mm-serial.h" #include "mm-serial-parsers.h" #include "mm-errors.h" -#include "mm-util.h" #include "mm-callback-info.h" static void impl_hso_authenticate (MMModemHso *self, @@ -27,9 +26,6 @@ static gpointer mm_modem_hso_parent_class = NULL; #define MM_MODEM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HSO, MMModemHsoPrivate)) typedef struct { - GRegex *connection_enabled_regex; - gpointer std_parser; - /* Pending connection attempt */ MMCallbackInfo *connect_pending_data; guint connect_pending_id; @@ -412,25 +408,20 @@ impl_hso_authenticate (MMModemHso *self, } static void -connection_enabled (const char *str, gpointer data) -{ - if (str && strlen (str) == 4) { - if (str[3] == '1') - connect_pending_done (MM_MODEM_HSO (data)); - if (str[3] == '0') - /* FIXME: disconnected. do something when we have modem status signals */ - ; - } -} - -static gboolean -hso_parse_response (gpointer data, GString *response, GError **error) +connection_enabled (MMSerial *serial, + GMatchInfo *info, + gpointer user_data) { - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (data); + char *str; - mm_util_strip_string (response, priv->connection_enabled_regex, connection_enabled, data); + str = g_match_info_fetch (info, 2); + if (str[0] == '1') + connect_pending_done (MM_MODEM_HSO (serial)); + else if (str[0] == '0') + /* FIXME: disconnected. do something when we have modem status signals */ + ; - return mm_serial_parser_v1_parse (priv->std_parser, response, error); + g_free (str); } /*****************************************************************************/ @@ -525,13 +516,13 @@ simple_connect (MMModemSimple *simple, static void mm_modem_hso_init (MMModemHso *self) { - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + GRegex *regex; - priv->connection_enabled_regex = g_regex_new ("_OWANCALL: (\\d, \\d)\\r\\n", - G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (self), TRUE); - priv->std_parser = (gpointer) mm_serial_parser_v1_new (); - mm_serial_set_response_parser (MM_SERIAL (self), hso_parse_response, self, NULL); + regex = g_regex_new ("_OWANCALL: (\\d), (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, connection_enabled, NULL, NULL); + g_regex_unref (regex); } static void @@ -585,14 +576,9 @@ constructor (GType type, static void finalize (GObject *object) { - MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (object); - /* Clear the pending connection if necessary */ connect_pending_done (MM_MODEM_HSO (object)); - g_regex_unref (priv->connection_enabled_regex); - mm_serial_parser_v1_destroy (priv->std_parser); - G_OBJECT_CLASS (mm_modem_hso_parent_class)->finalize (object); } diff --git a/plugins/mm-modem-huawei.c b/plugins/mm-modem-huawei.c index e607ce7f..0afb1168 100644 --- a/plugins/mm-modem-huawei.c +++ b/plugins/mm-modem-huawei.c @@ -7,30 +7,16 @@ #include "mm-modem-gsm-network.h" #include "mm-errors.h" #include "mm-callback-info.h" -#include "mm-util.h" #include "mm-serial-parsers.h" 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 { MMSerial *monitor_device; - gpointer std_parser; - - /* Unsolicited message regexps */ - GRegex *signal_quality_regex; - GRegex *mode_regex; - GRegex *status_regex; - GRegex *reg_state_regex; - - /* Pending operations */ - guint pending_registration; /* Cached state */ - MMModemGsmNetworkRegStatus reg_status; guint signal_quality; MMModemGsmNetworkMode mode; MMModemGsmNetworkBand band; @@ -53,195 +39,6 @@ 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) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - if (error) - info->error = g_error_copy (error); - 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); -} - -static void -enable (MMModem *modem, - gboolean do_enable, - MMModemFn callback, - gpointer user_data) -{ - MMModem *parent_modem_iface; - - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); - - if (do_enable) { - MMCallbackInfo *info; - - info = mm_callback_info_new (modem, callback, user_data); - parent_modem_iface->enable (modem, do_enable, parent_enable_done, info); - } else { - pending_registration_stop (MM_MODEM_HUAWEI (modem)); - parent_modem_iface->enable (modem, do_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, const char *reply, @@ -568,12 +365,19 @@ get_signal_quality (MMModemGsmNetwork *modem, } } -/* Unsolicited messages */ +/* Unsolicited message handlers */ static void -handle_signal_quality_change (const char *str, gpointer data) +handle_signal_quality_change (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data) { - int quality = atoi (str); + char *str; + int quality; + + str = g_match_info_fetch (match_info, 1); + quality = atoi (str); + g_free (str); if (quality == 99) /* 99 means unknown */ @@ -583,79 +387,66 @@ handle_signal_quality_change (const char *str, gpointer data) quality = quality * 100 / 31; g_debug ("Signal quality: %d", quality); - MM_MODEM_HUAWEI_GET_PRIVATE (data)->signal_quality = (guint32) quality; - mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (data), (guint32) quality); + MM_MODEM_HUAWEI_GET_PRIVATE (serial)->signal_quality = (guint32) quality; + mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (serial), (guint32) quality); } static void -handle_mode_change (const char *str, gpointer data) +handle_mode_change (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data) { + MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (serial); + char *str; int a; int b; - if (sscanf (str, "%d,%d", &a, &b)) { - MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (data); - - if (a == 3 && b == 2) - priv->mode = MM_MODEM_GSM_NETWORK_MODE_GPRS; - else if (a == 3 && b == 3) - priv->mode = MM_MODEM_GSM_NETWORK_MODE_EDGE; - else if (a == 5 && b == 4) - priv->mode = MM_MODEM_GSM_NETWORK_MODE_3G; - else if (a ==5 && b == 5) - priv->mode = MM_MODEM_GSM_NETWORK_MODE_HSDPA; - else { - g_warning ("Couldn't parse mode change value: '%s'", str); - return; - } - - g_debug ("Mode: %d", priv->mode); - mm_modem_gsm_network_mode (MM_MODEM_GSM_NETWORK (data), priv->mode); + str = g_match_info_fetch (match_info, 1); + a = atoi (str); + g_free (str); + + str = g_match_info_fetch (match_info, 2); + b = atoi (str); + g_free (str); + + if (a == 3 && b == 2) + priv->mode = MM_MODEM_GSM_NETWORK_MODE_GPRS; + else if (a == 3 && b == 3) + priv->mode = MM_MODEM_GSM_NETWORK_MODE_EDGE; + else if (a == 5 && b == 4) + priv->mode = MM_MODEM_GSM_NETWORK_MODE_3G; + else if (a == 5 && b == 5) + priv->mode = MM_MODEM_GSM_NETWORK_MODE_HSDPA; + else { + g_warning ("Couldn't parse mode change value: '%s'", str); + return; } + + g_debug ("Mode: %d", priv->mode); + mm_modem_gsm_network_mode (MM_MODEM_GSM_NETWORK (serial), priv->mode); } static void -handle_status_change (const char *str, gpointer data) +handle_status_change (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data) { + char *str; int n1, n2, n3, n4, n5, n6, n7; + str = g_match_info_fetch (match_info, 1); if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7)) { g_debug ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n", n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024); } -} - -static void -reg_state_changed (const char *str, gpointer data) -{ - reg_status_updated (MM_MODEM_HUAWEI (data), atoi (str)); -} - -static gboolean -huawei_parse_response (gpointer data, GString *response, GError **error) -{ - MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (data); - - mm_util_strip_string (response, priv->signal_quality_regex, handle_signal_quality_change, data); - mm_util_strip_string (response, priv->mode_regex, handle_mode_change, data); - mm_util_strip_string (response, priv->status_regex, handle_status_change, data); - mm_util_strip_string (response, priv->reg_state_regex, reg_state_changed, data); - - return mm_serial_parser_v1_parse (priv->std_parser, response, error); + g_free (str); } /*****************************************************************************/ static void -modem_init (MMModem *modem_class) -{ - modem_class->enable = enable; -} - -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; @@ -666,33 +457,21 @@ modem_gsm_network_init (MMModemGsmNetwork *class) static void 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); - priv->reg_state_regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + GRegex *regex; - priv->std_parser = mm_serial_parser_v1_new (); - mm_serial_set_response_parser (MM_SERIAL (self), huawei_parse_response, self, NULL); -} - -static void -finalize (GObject *object) -{ - MMModemHuaweiPrivate *priv = MM_MODEM_HUAWEI_GET_PRIVATE (object); + mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (self), TRUE); - pending_registration_stop (MM_MODEM_HUAWEI (object)); + regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, handle_signal_quality_change, NULL, NULL); + g_regex_unref (regex); - mm_serial_parser_v1_destroy (priv->std_parser); - g_regex_unref (priv->signal_quality_regex); - g_regex_unref (priv->mode_regex); - g_regex_unref (priv->status_regex); - g_regex_unref (priv->reg_state_regex); + regex = g_regex_new ("\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, handle_mode_change, NULL, NULL); + g_regex_unref (regex); - G_OBJECT_CLASS (mm_modem_huawei_parent_class)->finalize (object); + regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, handle_status_change, NULL, NULL); + g_regex_unref (regex); } static void @@ -702,9 +481,6 @@ mm_modem_huawei_class_init (MMModemHuaweiClass *klass) mm_modem_huawei_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (object_class, sizeof (MMModemHuaweiPrivate)); - - /* Virtual methods */ - object_class->finalize = finalize; } GType @@ -725,17 +501,11 @@ mm_modem_huawei_get_type (void) (GInstanceInitFunc) mm_modem_huawei_init, }; - static const GInterfaceInfo modem_iface_info = { - (GInterfaceInitFunc) modem_init - }; - static const GInterfaceInfo modem_gsm_network_info = { (GInterfaceInitFunc) modem_gsm_network_init }; modem_huawei_type = g_type_register_static (MM_TYPE_GENERIC_GSM, "MMModemHuawei", &modem_huawei_type_info, 0); - - g_type_add_interface_static (modem_huawei_type, MM_TYPE_MODEM, &modem_iface_info); g_type_add_interface_static (modem_huawei_type, MM_TYPE_MODEM_GSM_NETWORK, &modem_gsm_network_info); } diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index 6d52fc29..f527894e 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -39,17 +39,12 @@ #include "mm-serial-parsers.h" #include "mm-errors.h" #include "mm-callback-info.h" -#include "mm-util.h" static gpointer mm_modem_mbm_parent_class = NULL; #define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate)) typedef struct { - GRegex *boot_trig_regex; - GRegex *msg_waiting_regex; - GRegex *ciev_regex; - gpointer std_parser; guint32 signal_quality; } MMModemMbmPrivate; @@ -374,28 +369,34 @@ get_signal_quality (MMModemGsmNetwork *modem, /*****************************************************************************/ static void -boot_trig (const char *str, gpointer data) +boot_trig (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data) { - mm_serial_queue_command (MM_SERIAL(data), "AT*ENAP=1,1", 10, NULL, NULL); + mm_serial_queue_command (serial, "AT*ENAP=1,1", 10, NULL, NULL); } static void -ciev_trig (const char *str, gpointer data) +ciev_trig (MMSerial *serial, + GMatchInfo *match_info, + gpointer user_data) { + char *str; int event, value; guint32 quality; - if (!str) { - return; - } - + str = g_match_info_fetch (match_info, 1); event = str[0] - '0'; - value = str[2] - '0'; + g_free (str); + + str = g_match_info_fetch (match_info, 2); + value = str[0] - '0'; + g_free (str); switch (event) { case 2: /* signal quality, value 0-5 */ quality = value * 100 / 5; - mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (data), quality); + mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (serial), quality); break; case 9: /* roaming, value 0 or 1 */ g_debug ("%s: roaming %s\n", __FUNCTION__, value ? "active" : "inactive"); @@ -405,30 +406,22 @@ ciev_trig (const char *str, gpointer data) } } -static gboolean -mbm_parse_response (gpointer data, GString *response, GError **error) -{ - MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (data); - - mm_util_strip_string (response, priv->boot_trig_regex, boot_trig, data); - mm_util_strip_string (response, priv->ciev_regex, ciev_trig, data); - mm_util_strip_string (response, priv->msg_waiting_regex, NULL, data); - - return mm_serial_parser_v1_parse (priv->std_parser, response, error); -} - static void mm_modem_mbm_init (MMModemMbm *self) { - MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + GRegex *regex; - priv->boot_trig_regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - priv->msg_waiting_regex = g_regex_new ("\\r\\n[\\*]EMWI\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - priv->ciev_regex = g_regex_new ("\\r\\n\\+CIEV: (.,.)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, boot_trig, NULL, NULL); + g_regex_unref (regex); - priv->std_parser = (gpointer) mm_serial_parser_v1_new (); + regex = g_regex_new ("\\r\\n[\\*]EMWI\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, NULL, NULL, NULL); + g_regex_unref (regex); - mm_serial_set_response_parser (MM_SERIAL (self), mbm_parse_response, self, NULL); + regex = g_regex_new ("\\r\\n\\+CIEV: (\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, ciev_trig, NULL, NULL); + g_regex_unref (regex); } static void @@ -480,19 +473,6 @@ constructor (GType type, } static void -finalize (GObject *object) -{ - MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (object); - - mm_serial_parser_v1_destroy (priv->std_parser); - g_regex_unref (priv->boot_trig_regex); - g_regex_unref (priv->msg_waiting_regex); - g_regex_unref (priv->ciev_regex); - - G_OBJECT_CLASS (mm_modem_mbm_parent_class)->finalize (object); -} - -static void mm_modem_mbm_class_init (MMModemMbmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -502,7 +482,6 @@ mm_modem_mbm_class_init (MMModemMbmClass *klass) /* Virtual methods */ object_class->constructor = constructor; - object_class->finalize = finalize; } GType diff --git a/src/Makefile.am b/src/Makefile.am index 4ba3b750..caafef8b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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=$@ $< diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index a76cd698..b13623cd 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -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, @@ -47,6 +52,15 @@ mm_generic_gsm_new (const char *serial_device, const char *driver) } 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) { g_return_if_fail (MM_IS_GENERIC_GSM (modem)); @@ -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; - - if (sscanf (reply + 7, "%d,%d", &n, &stat)) { - MMModemGsmNetworkRegStatus status; - - 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; + int unsolicited, stat; + + if (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat)) { + reg_status_updated (MM_GENERIC_GSM (serial), stat); + + 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); diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index ab8bee07..d51f734d 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -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); diff --git a/src/mm-serial.c b/src/mm-serial.c index c81ca70d..3bd2b0d6 100644 --- a/src/mm-serial.c +++ b/src/mm-serial.c @@ -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) { @@ -436,6 +444,29 @@ mm_serial_queue_process (gpointer data) } 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, gpointer user_data, @@ -454,6 +485,58 @@ mm_serial_set_response_parser (MMSerial *self, } 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, GError **error) @@ -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); diff --git a/src/mm-serial.h b/src/mm-serial.h index 7d9e6961..e5e3d613 100644 --- a/src/mm-serial.h +++ b/src/mm-serial.h @@ -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, diff --git a/src/mm-util.c b/src/mm-util.c deleted file mode 100644 index 46badcf3..00000000 --- a/src/mm-util.c +++ /dev/null @@ -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); - } -} diff --git a/src/mm-util.h b/src/mm-util.h deleted file mode 100644 index 049b5f94..00000000 --- a/src/mm-util.h +++ /dev/null @@ -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 */ |