diff options
author | Tambet Ingo <tambet@gmail.com> | 2009-02-18 14:48:08 +0200 |
---|---|---|
committer | Tambet Ingo <tambet@gmail.com> | 2009-02-18 14:48:08 +0200 |
commit | 1215bd6a9d607018e0af7ad7afa85bc35cb713a0 (patch) | |
tree | cc33208fdbd4de15703fe8fefbd578f2d68908be | |
parent | 13facad4fa3be24d07768892135caebd88e95fcc (diff) |
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.
-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 */ |