aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-03-13 16:26:46 -0800
committerDan Williams <dcbw@redhat.com>2010-03-13 16:26:46 -0800
commitac7310ab1050701c07705e548c408d97aea76636 (patch)
tree98ae68fa1ee493bc3bc2caaa6fb38291355742fc /src
parent429c7cc661780d2848a97b092e1a0023e8c4d603 (diff)
gsm: add character set get/set support
Diffstat (limited to 'src')
-rw-r--r--src/mm-errors.c1
-rw-r--r--src/mm-errors.h3
-rw-r--r--src/mm-generic-gsm.c354
-rw-r--r--src/mm-modem.c93
-rw-r--r--src/mm-modem.h35
5 files changed, 466 insertions, 20 deletions
diff --git a/src/mm-errors.c b/src/mm-errors.c
index d0a71d65..c0ed608d 100644
--- a/src/mm-errors.c
+++ b/src/mm-errors.c
@@ -77,6 +77,7 @@ mm_modem_error_get_type (void)
ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_IN_PROGRESS, "OperationInProgress"),
ENUM_ENTRY (MM_MODEM_ERROR_REMOVED, "Removed"),
ENUM_ENTRY (MM_MODEM_ERROR_AUTHORIZATION_REQUIRED, "AuthorizationRequired"),
+ ENUM_ENTRY (MM_MODEM_ERROR_UNSUPPORTED_CHARSET, "UnsupportedCharset"),
{ 0, 0, 0 }
};
diff --git a/src/mm-errors.h b/src/mm-errors.h
index 1b924d40..15ac773b 100644
--- a/src/mm-errors.h
+++ b/src/mm-errors.h
@@ -40,7 +40,8 @@ enum {
MM_MODEM_ERROR_DISCONNECTED = 3,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS = 4,
MM_MODEM_ERROR_REMOVED = 5,
- MM_MODEM_ERROR_AUTHORIZATION_REQUIRED = 6
+ MM_MODEM_ERROR_AUTHORIZATION_REQUIRED = 6,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET = 7
};
#define MM_MODEM_ERROR (mm_modem_error_quark ())
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 0d55431d..27c09dbf 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -15,6 +15,7 @@
* Copyright (C) 2009 Ericsson
*/
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -84,6 +85,9 @@ typedef struct {
guint32 signal_quality;
gint cid;
+ guint32 charsets;
+ guint32 cur_charset;
+
MMAtSerialPort *primary;
MMAtSerialPort *secondary;
MMPort *data;
@@ -644,10 +648,99 @@ creg2_done (MMAtSerialPort *port,
}
static void
+enable_failed (MMModem *modem, GError *error, MMCallbackInfo *info)
+{
+ MMGenericGsmPrivate *priv;
+
+ info->error = mm_modem_check_removed (modem, error);
+
+ if (modem) {
+ mm_modem_set_state (modem,
+ MM_MODEM_STATE_DISABLED,
+ MM_MODEM_STATE_REASON_NONE);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (priv->primary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->primary)))
+ mm_serial_port_close (MM_SERIAL_PORT (priv->primary));
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close (MM_SERIAL_PORT (priv->secondary));
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static guint32 best_charsets[] = {
+ MM_MODEM_CHARSET_UTF8,
+ MM_MODEM_CHARSET_UCS2,
+ MM_MODEM_CHARSET_8859_1,
+ MM_MODEM_CHARSET_IRA,
+ MM_MODEM_CHARSET_UNKNOWN
+};
+
+static void
+enabled_set_charset_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint idx;
+
+ /* only modem removals are really a hard error */
+ if (error) {
+ if (!modem) {
+ enable_failed (modem, error, info);
+ return;
+ }
+
+ /* Try the next best charset */
+ idx = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "best-charset")) + 1;
+ if (best_charsets[idx] == MM_MODEM_CHARSET_UNKNOWN) {
+ GError *tmp_error;
+
+ /* No more character sets we can use */
+ tmp_error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Failed to find a usable modem character set");
+ enable_failed (modem, tmp_error, info);
+ g_error_free (tmp_error);
+ } else {
+ /* Send the new charset */
+ mm_callback_info_set_data (info, "best-charset", GUINT_TO_POINTER (idx), NULL);
+ mm_modem_set_charset (modem, best_charsets[idx], enabled_set_charset_done, info);
+ }
+ } else {
+ /* Modem is now enabled; update the state */
+ mm_generic_gsm_update_enabled_state (MM_GENERIC_GSM (modem), FALSE, MM_MODEM_STATE_REASON_NONE);
+
+ /* Set up unsolicited registration notifications */
+ mm_at_serial_port_queue_command (MM_GENERIC_GSM_GET_PRIVATE (modem)->primary,
+ "+CREG=2", 3, creg2_done, info);
+ }
+}
+
+static void
+supported_charsets_done (MMModem *modem,
+ guint32 charsets,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (!modem) {
+ enable_failed (modem, error, info);
+ return;
+ }
+
+ /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
+ mm_modem_set_charset (modem, MM_MODEM_CHARSET_UTF8, enabled_set_charset_done, info);
+}
+
+static void
get_allowed_mode_done (MMModem *modem,
- MMModemGsmAllowedMode mode,
- GError *error,
- gpointer user_data)
+ MMModemGsmAllowedMode mode,
+ GError *error,
+ gpointer user_data)
{
if (modem) {
mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (modem),
@@ -669,21 +762,8 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self,
priv = MM_GENERIC_GSM_GET_PRIVATE (self);
if (error) {
- mm_modem_set_state (MM_MODEM (self),
- MM_MODEM_STATE_DISABLED,
- MM_MODEM_STATE_REASON_NONE);
-
- if (priv->primary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->primary)))
- mm_serial_port_close (MM_SERIAL_PORT (priv->primary));
- if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
- mm_serial_port_close (MM_SERIAL_PORT (priv->secondary));
-
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
+ enable_failed ((MMModem *) self, error, info);
return;
- } else {
- /* Modem is enabled; update the state */
- mm_generic_gsm_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
}
/* Open the second port here if the modem has one. We'll use it for
@@ -709,8 +789,8 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self,
if (MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode)
MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode (self, get_allowed_mode_done, NULL);
- /* Set up unsolicited registration notifications */
- mm_at_serial_port_queue_command (priv->primary, "+CREG=2", 3, creg2_done, info);
+ /* And supported character sets */
+ mm_modem_get_supported_charsets (MM_MODEM (self), supported_charsets_done, info);
}
static void
@@ -2522,6 +2602,240 @@ set_allowed_mode (MMModemGsmNetwork *net,
}
/*****************************************************************************/
+/* Charset stuff */
+
+static void
+get_charsets_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+ GRegex *r = NULL;
+ GMatchInfo *match_info;
+ const char *p;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ /* Find the first '(' */
+ p = strchr (response->str, '(');
+ if (!p)
+ goto done;
+ p++;
+
+ /* Now parse each charset */
+ r = g_regex_new ("\\s*([^,\\)]+)\\s*", 0, 0, NULL);
+ if (!r)
+ goto done;
+
+ if (!g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL))
+ goto done;
+
+ priv->charsets = MM_MODEM_CHARSET_UNKNOWN;
+
+ while (g_match_info_matches (match_info)) {
+ char *str;
+
+ str = g_match_info_fetch (match_info, 1);
+ priv->charsets |= mm_modem_charset_from_string (str);
+ g_free (str);
+
+ g_match_info_next (match_info, NULL);
+ }
+ g_match_info_free (match_info);
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+
+done:
+ if (!info->error && !priv->charsets) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the supported character sets response");
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_supported_charsets (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port = priv->primary;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+
+ if (mm_port_get_connected (MM_PORT (priv->primary))) {
+ if (!priv->secondary) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot get serving system while connected");
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Use secondary port if primary is connected */
+ port = priv->secondary;
+ }
+
+ /* Use cached value if we have one */
+ if (MM_GENERIC_GSM_GET_PRIVATE (self)->charsets) {
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+ mm_callback_info_schedule (info);
+ } else
+ mm_at_serial_port_queue_command (port, "+CSCS=?", 3, get_charsets_done, info);
+}
+
+static void
+set_get_charset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+ MMModemCharset tried_charset;
+ const char *p;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ p = response->str;
+ if (g_str_has_prefix (p, "+CSCS:"))
+ p += 6;
+ while (*p == ' ')
+ p++;
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ priv->cur_charset = mm_modem_charset_from_string (p);
+
+ tried_charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset"));
+
+ if (tried_charset != priv->cur_charset) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Modem failed to change character set to %s",
+ mm_modem_charset_to_string (tried_charset));
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+#define TRIED_NO_QUOTES_TAG "tried-no-quotes"
+
+static void
+set_charset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ gboolean tried_no_quotes = !!mm_callback_info_get_data (info, TRIED_NO_QUOTES_TAG);
+ MMModemCharset charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset"));
+ char *command;
+
+ if (!info->modem || tried_no_quotes) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Some modems puke if you include the quotes around the character
+ * set name, so lets try it again without them.
+ */
+ mm_callback_info_set_data (info, TRIED_NO_QUOTES_TAG, GUINT_TO_POINTER (TRUE), NULL);
+ command = g_strdup_printf ("+CSCS=%s", mm_modem_charset_to_string (charset));
+ mm_at_serial_port_queue_command (port, command, 3, set_charset_done, info);
+ g_free (command);
+ } else
+ mm_at_serial_port_queue_command (port, "+CSCS?", 3, set_get_charset_done, info);
+}
+
+static gboolean
+check_for_single_value (guint32 value)
+{
+ gboolean found = FALSE;
+ guint32 i;
+
+ for (i = 1; i <= 32; i++) {
+ if (value & 0x1) {
+ if (found)
+ return FALSE; /* More than one bit set */
+ found = TRUE;
+ }
+ value >>= 1;
+ }
+
+ return TRUE;
+}
+
+static void
+set_charset (MMModem *modem,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ const char *str;
+ char *command;
+ MMAtSerialPort *port = priv->primary;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (!(priv->charsets & charset) || !check_for_single_value (charset)) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Character set 0x%X not supported",
+ charset);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ str = mm_modem_charset_to_string (charset);
+ if (!str) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Unhandled character set 0x%X",
+ charset);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ if (mm_port_get_connected (MM_PORT (priv->primary))) {
+ if (!priv->secondary) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot get set character set while connected");
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Use secondary port if primary is connected */
+ port = priv->secondary;
+ }
+
+ mm_callback_info_set_data (info, "charset", GUINT_TO_POINTER (charset), NULL);
+
+ command = g_strdup_printf ("+CSCS=\"%s\"", str);
+ mm_at_serial_port_queue_command (port, command, 3, set_charset_done, info);
+ g_free (command);
+}
+
+/*****************************************************************************/
/* MMModemGsmSms interface */
static void
@@ -2877,6 +3191,8 @@ modem_init (MMModem *modem_class)
modem_class->connect = connect;
modem_class->disconnect = disconnect;
modem_class->get_info = get_card_info;
+ modem_class->get_supported_charsets = get_supported_charsets;
+ modem_class->set_charset = set_charset;
}
static void
diff --git a/src/mm-modem.c b/src/mm-modem.c
index 8e2d8a48..6446e16d 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -14,6 +14,7 @@
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include "mm-modem.h"
@@ -471,6 +472,98 @@ impl_modem_get_info (MMModem *modem,
/*****************************************************************************/
+void
+mm_modem_get_supported_charsets (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->get_supported_charsets)
+ MM_MODEM_GET_INTERFACE (self)->get_supported_charsets (self, callback, user_data);
+ else {
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+ }
+}
+
+void
+mm_modem_set_charset (MMModem *self,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ g_return_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN);
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->set_charset)
+ MM_MODEM_GET_INTERFACE (self)->set_charset (self, charset, callback, user_data);
+ else {
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+ }
+}
+
+typedef struct {
+ const char *name;
+ MMModemCharset charset;
+} CharsetEntry;
+
+static CharsetEntry charset_map[] = {
+ { "UTF-8", MM_MODEM_CHARSET_UTF8 },
+ { "UCS2", MM_MODEM_CHARSET_UCS2 },
+ { "IRA", MM_MODEM_CHARSET_IRA },
+ { "GSM", MM_MODEM_CHARSET_GSM },
+ { "8859-1", MM_MODEM_CHARSET_8859_1 },
+ { "PCCP437", MM_MODEM_CHARSET_PCCP437 },
+ { "PCDN", MM_MODEM_CHARSET_PCDN },
+ { "HEX", MM_MODEM_CHARSET_HEX },
+ { NULL, MM_MODEM_CHARSET_UNKNOWN }
+};
+
+const char *
+mm_modem_charset_to_string (MMModemCharset charset)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ while (iter->name) {
+ if (iter->charset == charset)
+ return iter->name;
+ iter++;
+ }
+ g_warn_if_reached ();
+ return NULL;
+}
+
+MMModemCharset
+mm_modem_charset_from_string (const char *string)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (string != NULL, MM_MODEM_CHARSET_UNKNOWN);
+
+ while (iter->name) {
+ if (strcasestr (string, iter->name))
+ return iter->charset;
+ iter++;
+ }
+ return MM_MODEM_CHARSET_UNKNOWN;
+}
+
+/*****************************************************************************/
+
gboolean
mm_modem_owns_port (MMModem *self,
const char *subsys,
diff --git a/src/mm-modem.h b/src/mm-modem.h
index 93915eb0..5fc4bdcb 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -42,6 +42,18 @@ typedef enum {
MM_MODEM_STATE_REASON_NONE = 0
} MMModemStateReason;
+typedef enum {
+ MM_MODEM_CHARSET_UNKNOWN = 0x00000000,
+ MM_MODEM_CHARSET_GSM = 0x00000001,
+ MM_MODEM_CHARSET_IRA = 0x00000002,
+ MM_MODEM_CHARSET_8859_1 = 0x00000004,
+ MM_MODEM_CHARSET_UTF8 = 0x00000008,
+ MM_MODEM_CHARSET_UCS2 = 0x00000010,
+ MM_MODEM_CHARSET_PCCP437 = 0x00000020,
+ MM_MODEM_CHARSET_PCDN = 0x00000040,
+ MM_MODEM_CHARSET_HEX = 0x00000080
+} MMModemCharset;
+
#define DBUS_PATH_TAG "dbus-path"
#define MM_TYPE_MODEM (mm_modem_get_type ())
@@ -158,6 +170,16 @@ struct _MMModem {
MMModemInfoFn callback,
gpointer user_data);
+ void (*get_supported_charsets) (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*set_charset) (MMModem *self,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data);
+
+
/* Normally implemented by the modem base class; plugins should
* never need to implement this.
*/
@@ -222,6 +244,19 @@ void mm_modem_get_info (MMModem *self,
MMModemInfoFn callback,
gpointer user_data);
+void mm_modem_get_supported_charsets (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_set_charset (MMModem *self,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data);
+
+const char *mm_modem_charset_to_string (MMModemCharset charset);
+
+MMModemCharset mm_modem_charset_from_string (const char *string);
+
gboolean mm_modem_get_valid (MMModem *self);
char *mm_modem_get_device (MMModem *self);