Re: [RFC PATCH] ModemManager: fix USSD reception, network notifications, and network requests
- From: Dan Williams <dcbw redhat com>
- To: networkmanager-list gnome org
- Subject: Re: [RFC PATCH] ModemManager: fix USSD reception, network notifications, and network requests
- Date: Wed, 15 Jun 2011 13:07:44 -0500
On Thu, 2011-06-09 at 16:33 -0500, Dan Williams wrote:
> RFC patch; I'm tired and don't want to look at this anymore so somebody
> else double-check the cases for me. Because the code was sending the
> USSD request with a "notify me via unsolicited result code" tag, the
> response could come from any port, and in was coming from other ports on
> various devices. But the code expected the response on the main port,
> thus it got lost.
>
> So turn the USSD response processing into an unsolicited command handler
> instead, which allows us to process the response no matter where it
> comes from. This patch also enables network-initiated USSD
> notifications and requests since that's easy to add once the first thing
> here is done.
>
> I'm somewhat wary about having to cache the MMCallbackInfo from
> user-originated requests, since tracking the lifetime of those is easy
> to screw up. So any double-checking of that would be much appreciated.
I've pushed this.
Dan
> ---
> diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
> index bc718d1..d814c57 100644
> --- a/src/mm-generic-gsm.c
> +++ b/src/mm-generic-gsm.c
> @@ -120,7 +120,11 @@ typedef struct {
> gboolean loc_enabled;
> gboolean loc_signal;
>
> + gboolean ussd_enabled;
> + MMCallbackInfo *pending_ussd_info;
> MMModemGsmUssdState ussd_state;
> + char *ussd_network_request;
> + char *ussd_network_notification;
>
> /* SMS */
> GHashTable *sms_present;
> @@ -174,6 +178,10 @@ static void cmti_received (MMAtSerialPort *port,
> GMatchInfo *info,
> gpointer user_data);
>
> +static void cusd_received (MMAtSerialPort *port,
> + GMatchInfo *info,
> + gpointer user_data);
> +
> #define GS_HASH_TAG "get-sms"
> static GValue *simple_string_value (const char *str);
> static GValue *simple_uint_value (guint32 i);
> @@ -844,6 +852,10 @@ mm_generic_gsm_grab_port (MMGenericGsm *self,
> mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, cmti_received, self, NULL);
> g_regex_unref (regex);
>
> + regex = g_regex_new ("\\r\\n\\+CUSD:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
> + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, cusd_received, self, NULL);
> + g_regex_unref (regex);
> +
> if (ptype == MM_PORT_TYPE_PRIMARY) {
> priv->primary = MM_AT_SERIAL_PORT (port);
> if (!priv->data) {
> @@ -1412,6 +1424,21 @@ cind_cb (MMAtSerialPort *port,
> }
> }
>
> +static void
> +cusd_enable_cb (MMAtSerialPort *port,
> + GString *response,
> + GError *error,
> + gpointer user_data)
> +{
> + if (error) {
> + mm_warn ("(%s): failed to enable USSD notifications.",
> + mm_port_get_device (MM_PORT (port)));
> + return;
> + }
> +
> + MM_GENERIC_GSM_GET_PRIVATE (user_data)->ussd_enabled = TRUE;
> +}
> +
> void
> mm_generic_gsm_enable_complete (MMGenericGsm *self,
> GError *error,
> @@ -1462,6 +1489,9 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self,
> mm_at_serial_port_queue_command (priv->primary, cmd, 3, NULL, NULL);
> g_free (cmd);
>
> + /* Enable USSD notifications */
> + mm_at_serial_port_queue_command (priv->primary, "+CUSD=1", 3, cusd_enable_cb, self);
> +
> mm_at_serial_port_queue_command (priv->primary, "+CIND=?", 3, cind_cb, self);
>
> /* Try one more time to get the SIM ID */
> @@ -1689,6 +1719,11 @@ disable_flash_done (MMSerialPort *port,
> mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CREG=0", 3, NULL, NULL);
> mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CGREG=0", 3, NULL, NULL);
>
> + if (priv->ussd_enabled) {
> + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CUSD=0", 3, NULL, NULL);
> + priv->ussd_enabled = FALSE;
> + }
> +
> if (priv->cmer_enabled) {
> mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CMER=0", 3, NULL, NULL);
>
> @@ -1732,6 +1767,8 @@ disable (MMModem *modem,
>
> mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
>
> + mm_generic_gsm_ussd_cleanup (MM_GENERIC_GSM (modem));
> +
> if (priv->poll_id) {
> g_source_remove (priv->poll_id);
> priv->poll_id = 0;
> @@ -4676,93 +4713,171 @@ ussd_update_state (MMGenericGsm *self, MMModemGsmUssdState new_state)
> }
> }
>
> +void
> +mm_generic_gsm_ussd_cleanup (MMGenericGsm *self)
> +{
> + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
> +
> + if (priv->pending_ussd_info) {
> + /* And schedule the callback */
> + g_clear_error (&priv->pending_ussd_info->error);
> + priv->pending_ussd_info->error = g_error_new_literal (MM_MODEM_ERROR,
> + MM_MODEM_ERROR_GENERAL,
> + "USSD session terminated without reply.");
> + mm_callback_info_schedule (priv->pending_ussd_info);
> + priv->pending_ussd_info = NULL;
> + }
> +
> + ussd_update_state (self, MM_MODEM_GSM_USSD_STATE_IDLE);
> +
> + g_free (priv->ussd_network_request);
> + priv->ussd_network_request = NULL;
> + g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_REQUEST);
> +
> + g_free (priv->ussd_network_notification);
> + priv->ussd_network_notification = NULL;
> + g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION);
> +}
> +
> +static char *
> +decode_ussd_response (const char *reply, MMModemCharset cur_charset)
> +{
> + char **items, **iter, *p;
> + char *str = NULL;
> + gint encoding = -1;
> +
> + /* Look for the first ',' */
> + p = strchr (reply, ',');
> + if (p == NULL)
> + return NULL;
> +
> + items = g_strsplit_set (p + 1, " ,", -1);
> + for (iter = items; iter && *iter; iter++) {
> + if (*iter[0] == '\0')
> + continue;
> + if (str == NULL)
> + str = *iter;
> + else if (encoding == -1) {
> + encoding = atoi (*iter);
> + mm_dbg ("USSD data coding scheme %d", encoding);
> + break; /* All done */
> + }
> + }
> +
> + /* Strip quotes */
> + if (str[0] == '"')
> + str++;
> + p = strchr (str, '"');
> + if (p)
> + *p = '\0';
> +
> + /* FIXME: actually use the given encoding scheme */
> + return mm_modem_charset_hex_to_utf8 (str, cur_charset);
> +}
> +
> static void
> -ussd_send_done (MMAtSerialPort *port,
> - GString *response,
> - GError *error,
> - gpointer user_data)
> +cusd_received (MMAtSerialPort *port,
> + GMatchInfo *info,
> + gpointer user_data)
> {
> - MMCallbackInfo *info = (MMCallbackInfo *) user_data;
> - MMGenericGsmPrivate *priv;
> + MMGenericGsm *self = MM_GENERIC_GSM (user_data);
> + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
> + GError *error = NULL;
> gint status;
> - gboolean parsed = FALSE;
> MMModemGsmUssdState ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
> - const char *str, *start = NULL, *end = NULL;
> char *reply = NULL, *converted;
>
> - /* If the modem has already been removed, return without
> - * scheduling callback */
> - if (mm_callback_info_check_modem_removed (info))
> + reply = g_match_info_fetch (info, 1);
> + if (!reply || !isdigit (*reply)) {
> + mm_warn ("Recieved invalid USSD response: '%s'", reply ? reply : "(none)");
> + g_free (reply);
> return;
> -
> - if (error) {
> - info->error = g_error_copy (error);
> - goto done;
> }
>
> - priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
> - ussd_state = priv->ussd_state;
> -
> - str = mm_strip_tag (response->str, "+CUSD:");
> - if (!str || !isdigit (*str))
> - goto done;
> -
> - status = g_ascii_digit_value (*str);
> + status = g_ascii_digit_value (*reply);
> switch (status) {
> case 0: /* no further action required */
> - ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
> + converted = decode_ussd_response (reply, priv->cur_charset);
> + if (priv->pending_ussd_info) {
> + /* Response to the user's request */
> + mm_callback_info_set_result (priv->pending_ussd_info, converted, g_free);
> + } else {
> + /* Network-initiated USSD-Notify */
> + g_free (priv->ussd_network_notification);
> + priv->ussd_network_notification = converted;
> + g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION);
> + }
> break;
> case 1: /* further action required */
> ussd_state = MM_MODEM_GSM_USSD_STATE_USER_RESPONSE;
> + converted = decode_ussd_response (reply, priv->cur_charset);
> + if (priv->pending_ussd_info) {
> + mm_callback_info_set_result (priv->pending_ussd_info, converted, g_free);
> + } else {
> + /* Network-initiated USSD-Request */
> + g_free (priv->ussd_network_request);
> + priv->ussd_network_request = converted;
> + g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_REQUEST);
> + }
> break;
> case 2:
> - info->error = g_error_new (MM_MODEM_ERROR,
> - MM_MODEM_ERROR_GENERAL,
> - "USSD terminated by network.");
> - ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
> + error = g_error_new_literal (MM_MODEM_ERROR,
> + MM_MODEM_ERROR_GENERAL,
> + "USSD terminated by network.");
> break;
> case 4:
> - info->error = g_error_new (MM_MODEM_ERROR,
> - MM_MODEM_ERROR_GENERAL,
> - "Operiation not supported.");
> - ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
> + error = g_error_new_literal (MM_MODEM_ERROR,
> + MM_MODEM_ERROR_GENERAL,
> + "Operation not supported.");
> break;
> default:
> - info->error = g_error_new (MM_MODEM_ERROR,
> - MM_MODEM_ERROR_GENERAL,
> - "Unknown USSD reply %d", status);
> - ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
> + error = g_error_new (MM_MODEM_ERROR,
> + MM_MODEM_ERROR_GENERAL,
> + "Unhandled USSD reply %d", status);
> break;
> }
> - if (info->error)
> - goto done;
>
> - /* look for the reply */
> - if ((start = strchr (str, '"')) && (end = strrchr (str, '"')) && (start != end))
> - reply = g_strndup (start + 1, end - start -1);
> + ussd_update_state (self, ussd_state);
>
> - if (reply) {
> - /* look for the reply data coding scheme */
> - if ((start = strrchr (end, ',')) != NULL)
> - mm_dbg ("USSD data coding scheme %d", atoi (start + 1));
> -
> - converted = mm_modem_charset_hex_to_utf8 (reply, priv->cur_charset);
> - mm_callback_info_set_result (info, converted, g_free);
> - parsed = TRUE;
> - g_free (reply);
> + if (priv->pending_ussd_info) {
> + if (error)
> + priv->pending_ussd_info->error = g_error_copy (error);
> + mm_callback_info_schedule (priv->pending_ussd_info);
> + priv->pending_ussd_info = NULL;
> }
>
> -done:
> - if (!parsed && !info->error) {
> - info->error = g_error_new (MM_MODEM_ERROR,
> - MM_MODEM_ERROR_GENERAL,
> - "Could not parse USSD reply '%s'",
> - response->str);
> + g_clear_error (&error);
> + g_free (reply);
> +}
> +
> +static void
> +ussd_send_done (MMAtSerialPort *port,
> + GString *response,
> + GError *error,
> + gpointer user_data)
> +{
> + MMCallbackInfo *info = (MMCallbackInfo *) user_data;
> + MMGenericGsmPrivate *priv;
> +
> + /* If the modem has already been removed, return without
> + * scheduling callback */
> + if (mm_callback_info_check_modem_removed (info))
> + return;
> +
> + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
> +
> + if (error) {
> + /* Some immediate error happened when sending the USSD request */
> + info->error = g_error_copy (error);
> + priv->pending_ussd_info = NULL;
> + mm_callback_info_schedule (info);
> +
> + ussd_update_state (MM_GENERIC_GSM (info->modem), MM_MODEM_GSM_USSD_STATE_IDLE);
> }
> - mm_callback_info_schedule (info);
>
> - if (info->modem)
> - ussd_update_state (MM_GENERIC_GSM (info->modem), ussd_state);
> + /* Otherwise if no error wait for the response to show up via the
> + * unsolicited response code.
> + */
> }
>
> static void
> @@ -4779,6 +4894,8 @@ ussd_send (MMModemGsmUssd *modem,
> MMAtSerialPort *port;
> gboolean success;
>
> + g_warn_if_fail (priv->pending_ussd_info == NULL);
> +
> info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
>
> port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
> @@ -4787,6 +4904,9 @@ ussd_send (MMModemGsmUssd *modem,
> return;
> }
>
> + /* Cache the callback info since the response is an unsolicited one */
> + priv->pending_ussd_info = info;
> +
> /* encode to cur_charset */
> success = mm_modem_charset_byte_array_append (ussd_command, command, FALSE, priv->cur_charset);
> g_warn_if_fail (success == TRUE);
> @@ -5737,10 +5857,10 @@ get_property (GObject *object, guint prop_id,
> g_value_set_string (value, ussd_state_to_string (priv->ussd_state));
> break;
> case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST:
> - g_value_set_string (value, "");
> + g_value_set_string (value, priv->ussd_network_request);
> break;
> case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION:
> - g_value_set_string (value, "");
> + g_value_set_string (value, priv->ussd_network_notification);
> break;
> case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD:
> /* By default, try to set XOFF/XON flow control */
> @@ -5769,6 +5889,7 @@ finalize (GObject *object)
> MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object);
>
> mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (object));
> + mm_generic_gsm_ussd_cleanup (MM_GENERIC_GSM (object));
>
> if (priv->pin_check_timeout) {
> g_source_remove (priv->pin_check_timeout);
> diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
> index 5afab3c..57b65bf 100644
> --- a/src/mm-generic-gsm.h
> +++ b/src/mm-generic-gsm.h
> @@ -159,6 +159,8 @@ MMModem *mm_generic_gsm_new (const char *device,
>
> void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
>
> +void mm_generic_gsm_ussd_cleanup (MMGenericGsm *modem);
> +
> gint mm_generic_gsm_get_cid (MMGenericGsm *modem);
>
> void mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
>
>
> _______________________________________________
> networkmanager-list mailing list
> networkmanager-list gnome org
> http://mail.gnome.org/mailman/listinfo/networkmanager-list
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]