aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:23 +0100
committerGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:23 +0100
commitdc645b92b9a7db3076ae34986ac219d01677d124 (patch)
tree963a5d6ad150a88a2a8ab6d994d79d539e19383a /src
parent87bd9deec22af69bb27226254803ac5c63b18d78 (diff)
Imported Upstream version 0.4+git.20100624t180933.6e79d15upstream/0.4+git.20100624t180933.6e79d15
Diffstat (limited to 'src')
-rw-r--r--src/77-mm-pcmcia-device-blacklist.rules10
-rw-r--r--src/77-mm-platform-serial-whitelist.rules14
-rw-r--r--src/77-mm-usb-device-blacklist.rules66
-rw-r--r--src/Makefile.am65
-rw-r--r--src/main.c27
-rw-r--r--src/mm-at-serial-port.c364
-rw-r--r--src/mm-at-serial-port.h85
-rw-r--r--src/mm-auth-provider-factory.c45
-rw-r--r--src/mm-auth-provider-polkit.c153
-rw-r--r--src/mm-auth-provider-polkit.h43
-rw-r--r--src/mm-auth-provider.c300
-rw-r--r--src/mm-auth-provider.h86
-rw-r--r--src/mm-auth-request-polkit.c175
-rw-r--r--src/mm-auth-request-polkit.h53
-rw-r--r--src/mm-auth-request.c182
-rw-r--r--src/mm-auth-request.h72
-rw-r--r--src/mm-callback-info.c23
-rw-r--r--src/mm-callback-info.h6
-rw-r--r--src/mm-charsets.c175
-rw-r--r--src/mm-charsets.h52
-rw-r--r--src/mm-errors.c185
-rw-r--r--src/mm-errors.h18
-rw-r--r--src/mm-generic-cdma.c975
-rw-r--r--src/mm-generic-cdma.h35
-rw-r--r--src/mm-generic-gsm.c2833
-rw-r--r--src/mm-generic-gsm.h98
-rw-r--r--src/mm-manager.c499
-rw-r--r--src/mm-manager.h4
-rw-r--r--src/mm-modem-base.c431
-rw-r--r--src/mm-modem-base.h33
-rw-r--r--src/mm-modem-cdma.c35
-rw-r--r--src/mm-modem-gsm-card.c349
-rw-r--r--src/mm-modem-gsm-card.h25
-rw-r--r--src/mm-modem-gsm-network.c175
-rw-r--r--src/mm-modem-gsm-network.h45
-rw-r--r--src/mm-modem-gsm-sms.c384
-rw-r--r--src/mm-modem-gsm.h48
-rw-r--r--src/mm-modem-helpers.c602
-rw-r--r--src/mm-modem-helpers.h32
-rw-r--r--src/mm-modem-location.c330
-rw-r--r--src/mm-modem-location.h73
-rw-r--r--src/mm-modem.c201
-rw-r--r--src/mm-modem.h71
-rw-r--r--src/mm-plugin-base.c419
-rw-r--r--src/mm-plugin-base.h13
-rw-r--r--src/mm-plugin.c14
-rw-r--r--src/mm-plugin.h6
-rw-r--r--src/mm-port.c31
-rw-r--r--src/mm-port.h5
-rw-r--r--src/mm-qcdm-serial-port.c225
-rw-r--r--src/mm-qcdm-serial-port.h66
-rw-r--r--src/mm-serial-parsers.c102
-rw-r--r--src/mm-serial-port.c610
-rw-r--r--src/mm-serial-port.h92
-rw-r--r--src/mm-utils.c78
-rw-r--r--src/mm-utils.h24
-rw-r--r--src/tests/test-modem-helpers.c453
57 files changed, 9963 insertions, 1652 deletions
diff --git a/src/77-mm-pcmcia-device-blacklist.rules b/src/77-mm-pcmcia-device-blacklist.rules
new file mode 100644
index 0000000..76259a2
--- /dev/null
+++ b/src/77-mm-pcmcia-device-blacklist.rules
@@ -0,0 +1,10 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_pcmcia_device_blacklist_end"
+SUBSYSTEM!="pcmcia", GOTO="mm_pcmcia_device_blacklist_end"
+
+# Gemplus Serial Port smartcard adapter
+ATTRS{prod_id1}=="Gemplus", ATTRS{prod_id2}=="SerialPort", ATTRS{prod_id3}=="GemPC Card", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+LABEL="mm_pcmcia_device_blacklist_end"
+
diff --git a/src/77-mm-platform-serial-whitelist.rules b/src/77-mm-platform-serial-whitelist.rules
new file mode 100644
index 0000000..b62d0a6
--- /dev/null
+++ b/src/77-mm-platform-serial-whitelist.rules
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_platform_device_whitelist_end"
+SUBSYSTEM!="platform", GOTO="mm_platform_device_whitelist_end"
+
+# Be careful here since many devices connected to platform drivers on PCs
+# are legacy devices that won't like probing. But often on embedded
+# systems serial ports are provided by platform devices.
+
+# Allow atmel_usart
+DRIVERS=="atmel_usart", ENV{ID_MM_PLATFORM_DRIVER_PROBE}="1"
+
+LABEL="mm_platform_device_whitelist_end"
+
diff --git a/src/77-mm-usb-device-blacklist.rules b/src/77-mm-usb-device-blacklist.rules
new file mode 100644
index 0000000..78a6770
--- /dev/null
+++ b/src/77-mm-usb-device-blacklist.rules
@@ -0,0 +1,66 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_usb_device_blacklist_end"
+SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
+ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
+
+# APC UPS devices
+ATTRS{idVendor}=="051d", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Sweex 1000VA
+ATTRS{idVendor}=="0925", ATTRS{idProduct}=="1234", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Agiler UPS
+ATTRS{idVendor}=="05b8", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Krauler UP-M500VA
+ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Ablerex 625L USB
+ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Belkin F6C1200-UNV
+ATTRS{idVendor}=="0665", ATTRS{idProduct}=="5161", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various Liebert and Phoenixtec Power devices
+ATTRS{idVendor}=="06da", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Unitek Alpha 1200Sx
+ATTRS{idVendor}=="0f03", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various Tripplite devices
+ATTRS{idVendor}=="09ae", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various MGE Office Protection Systems devices
+ATTRS{idVendor}=="0463", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="0463", ATTRS{idProduct}=="ffff", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# CyberPower 900AVR/BC900D
+ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0005", ENV{ID_MM_DEVICE_IGNORE}="1"
+# CyberPower CP1200AVR/BC1200D
+ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0501", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various Belkin devices
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0980", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0900", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0910", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0912", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0551", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0751", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0375", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="1100", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# HP R/T 2200 INTL (like SMART2200RMXL2U)
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f0a", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Powerware devices
+ATTRS{idVendor}=="0592", ATTRS{idProduct}=="0002", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Palm Treo 700/900/etc
+# Shouldn't be probed themselves, but you can install programs like
+# "MobileStream USB Modem" which changes the USB PID of the device to something
+# that isn't blacklisted.
+ATTRS{idVendor}=="0830", ATTRS{idProduct}=="0061", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+LABEL="mm_usb_device_blacklist_end"
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 9209b55..2061ae8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,14 @@
SUBDIRS=. tests
+udevrulesdir = $(UDEV_BASE_DIR)/rules.d
+udevrules_DATA = \
+ 77-mm-usb-device-blacklist.rules \
+ 77-mm-pcmcia-device-blacklist.rules \
+ 77-mm-platform-serial-whitelist.rules
+
+EXTRA_DIST = \
+ $(udevrules_DATA)
+
noinst_LTLIBRARIES = libmodem-helpers.la
libmodem_helpers_la_CPPFLAGS = \
@@ -9,38 +18,76 @@ libmodem_helpers_la_SOURCES = \
mm-errors.c \
mm-errors.h \
mm-modem-helpers.c \
- mm-modem-helpers.h
+ mm-modem-helpers.h \
+ mm-charsets.c \
+ mm-charsets.h \
+ mm-utils.c \
+ mm-utils.h
sbin_PROGRAMS = modem-manager
modem_manager_CPPFLAGS = \
$(MM_CFLAGS) \
$(GUDEV_CFLAGS) \
+ -I$(top_srcdir) \
-I${top_builddir}/marshallers \
-DPLUGINDIR=\"$(pkglibdir)\"
+if WITH_POLKIT
+modem_manager_CPPFLAGS += $(POLKIT_CFLAGS)
+endif
+
modem_manager_LDADD = \
$(MM_LIBS) \
$(GUDEV_LIBS) \
$(top_builddir)/marshallers/libmarshallers.la \
+ $(top_builddir)/libqcdm/src/libqcdm.la \
$(builddir)/libmodem-helpers.la
+if WITH_POLKIT
+modem_manager_LDADD += $(POLKIT_LIBS)
+endif
+
+auth_sources = \
+ mm-auth-request.c \
+ mm-auth-request.h \
+ mm-auth-provider.h \
+ mm-auth-provider.c \
+ mm-auth-provider-factory.c
+
+if WITH_POLKIT
+auth_sources += \
+ mm-auth-request-polkit.c \
+ mm-auth-request-polkit.h \
+ mm-auth-provider-polkit.c \
+ mm-auth-provider-polkit.h
+endif
+
+loc_sources = \
+ mm-modem-location.c \
+ mm-modem-location.h
+
modem_manager_SOURCES = \
main.c \
mm-callback-info.c \
mm-callback-info.h \
+ $(auth_sources) \
mm-manager.c \
mm-manager.h \
mm-modem.c \
mm-modem.h \
mm-port.c \
mm-port.h \
- mm-modem-base.c \
- mm-modem-base.h \
mm-serial-port.c \
mm-serial-port.h \
+ mm-at-serial-port.c \
+ mm-at-serial-port.h \
+ mm-qcdm-serial-port.c \
+ mm-qcdm-serial-port.h \
mm-serial-parsers.c \
mm-serial-parsers.h \
+ mm-modem-base.c \
+ mm-modem-base.h \
mm-generic-cdma.c \
mm-generic-cdma.h \
mm-generic-gsm.c \
@@ -86,7 +133,6 @@ mm-modem-gsm-network-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-network.xm
mm-modem-gsm-sms-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-sms.xml
dbus-binding-tool --prefix=mm_modem_gsm_sms --mode=glib-server --output=$@ $<
-
BUILT_SOURCES = \
mm-manager-glue.h \
mm-modem-glue.h \
@@ -96,4 +142,15 @@ BUILT_SOURCES = \
mm-modem-gsm-network-glue.h \
mm-modem-gsm-sms-glue.h
+if WITH_LOCATION_API
+mm-modem-location-glue.h: $(top_srcdir)/introspection/mm-modem-location.xml
+ dbus-binding-tool --prefix=mm_modem_location --mode=glib-server --output=$@ $<
+
+modem_manager_SOURCES += $(loc_sources)
+
+BUILT_SOURCES += mm-modem-location-glue.h
+else
+EXTRA_DIST += $(loc_sources)
+endif
+
CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/main.c b/src/main.c
index 3669115..72fa6dc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,18 +11,22 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <signal.h>
#include <syslog.h>
#include <string.h>
+#include <unistd.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "mm-manager.h"
#include "mm-options.h"
-#define HAL_DBUS_SERVICE "org.freedesktop.Hal"
+#if !defined(MM_DIST_VERSION)
+# define MM_DIST_VERSION VERSION
+#endif
static GMainLoop *loop = NULL;
@@ -33,8 +37,11 @@ mm_signal_handler (int signo)
mm_options_set_debug (!mm_options_debug ());
else if (signo == SIGINT || signo == SIGTERM) {
g_message ("Caught signal %d, shutting down...", signo);
- g_main_loop_quit (loop);
- }
+ if (loop)
+ g_main_loop_quit (loop);
+ else
+ _exit (0);
+ }
}
static void
@@ -178,6 +185,8 @@ main (int argc, char *argv[])
if (!mm_options_debug ())
logging_setup ();
+ g_message ("ModemManager (version " MM_DIST_VERSION ") starting...");
+
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
if (!bus) {
g_warning ("Could not get the system bus. Make sure "
@@ -201,6 +210,16 @@ main (int argc, char *argv[])
g_signal_handler_disconnect (proxy, id);
+ mm_manager_shutdown (manager);
+
+ /* Wait for all modems to be removed */
+ while (mm_manager_num_modems (manager)) {
+ GMainContext *ctx = g_main_loop_get_context (loop);
+
+ g_main_context_iteration (ctx, FALSE);
+ g_usleep (50);
+ }
+
g_object_unref (manager);
g_object_unref (proxy);
dbus_g_connection_unref (bus);
diff --git a/src/mm-at-serial-port.c b/src/mm-at-serial-port.c
new file mode 100644
index 0000000..068450d
--- /dev/null
+++ b/src/mm-at-serial-port.c
@@ -0,0 +1,364 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#define _GNU_SOURCE /* for strcasestr() */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-at-serial-port.h"
+#include "mm-errors.h"
+#include "mm-options.h"
+
+G_DEFINE_TYPE (MMAtSerialPort, mm_at_serial_port, MM_TYPE_SERIAL_PORT)
+
+#define MM_AT_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortPrivate))
+
+typedef struct {
+ /* Response parser data */
+ MMAtSerialResponseParserFn response_parser_fn;
+ gpointer response_parser_user_data;
+ GDestroyNotify response_parser_notify;
+ GSList *unsolicited_msg_handlers;
+} MMAtSerialPortPrivate;
+
+
+/*****************************************************************************/
+
+void
+mm_at_serial_port_set_response_parser (MMAtSerialPort *self,
+ MMAtSerialResponseParserFn fn,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+
+ if (priv->response_parser_notify)
+ priv->response_parser_notify (priv->response_parser_user_data);
+
+ priv->response_parser_fn = fn;
+ priv->response_parser_user_data = user_data;
+ priv->response_parser_notify = notify;
+}
+
+static gboolean
+parse_response (MMSerialPort *port, GByteArray *response, GError **error)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+ gboolean found;
+ GString *string;
+
+ g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
+
+ /* Construct the string that AT-parsing functions expect */
+ string = g_string_sized_new (response->len + 1);
+ g_string_append_len (string, (const char *) response->data, response->len);
+
+ /* Parse it */
+ found = priv->response_parser_fn (priv->response_parser_user_data, string, error);
+
+ /* And copy it back into the response array after the parser has removed
+ * matches and cleaned it up.
+ */
+ if (response->len)
+ g_byte_array_remove_range (response, 0, response->len);
+ g_byte_array_append (response, (const guint8 *) string->str, string->len);
+ g_string_free (string, TRUE);
+ return found;
+}
+
+static gsize
+handle_response (MMSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
+ MMAtSerialResponseFn response_callback = (MMAtSerialResponseFn) callback;
+ GString *string;
+
+ /* Convert to a string and call the callback */
+ string = g_string_sized_new (response->len + 1);
+ g_string_append_len (string, (const char *) response->data, response->len);
+ response_callback (self, string, error, callback_data);
+ g_string_free (string, TRUE);
+
+ return response->len;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ GRegex *regex;
+ MMAtSerialUnsolicitedMsgFn callback;
+ gpointer user_data;
+ GDestroyNotify notify;
+} MMAtUnsolicitedMsgHandler;
+
+void
+mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
+ GRegex *regex,
+ MMAtSerialUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMAtUnsolicitedMsgHandler *handler;
+ MMAtSerialPortPrivate *priv;
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (regex != NULL);
+
+ handler = g_slice_new (MMAtUnsolicitedMsgHandler);
+ handler->regex = g_regex_ref (regex);
+ handler->callback = callback;
+ handler->user_data = user_data;
+ handler->notify = notify;
+
+ priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+ priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
+}
+
+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 (MMSerialPort *port, GByteArray *response)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
+ MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data;
+ GMatchInfo *match_info;
+ gboolean matches;
+
+ matches = g_regex_match_full (handler->regex,
+ (const char *) response->data,
+ 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,
+ (const char *) response->data,
+ response->len,
+ 0, 0,
+ remove_eval_cb, &result_len, NULL);
+
+ g_byte_array_remove_range (response, 0, response->len);
+ g_byte_array_append (response, (const guint8 *) str, result_len);
+ g_free (str);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+static GByteArray *
+at_command_to_byte_array (const char *command)
+{
+ GByteArray *buf;
+ int cmdlen;
+
+ g_return_val_if_fail (command != NULL, NULL);
+
+ cmdlen = strlen (command);
+ buf = g_byte_array_sized_new (cmdlen + 3);
+
+ /* Make sure there's an AT in the front */
+ if (!g_str_has_prefix (command, "AT"))
+ g_byte_array_append (buf, (const guint8 *) "AT", 2);
+ g_byte_array_append (buf, (const guint8 *) command, cmdlen);
+
+ /* Make sure there's a trailing carriage return */
+ if (command[cmdlen] != '\r')
+ g_byte_array_append (buf, (const guint8 *) "\r", 1);
+
+ return buf;
+}
+
+void
+mm_at_serial_port_queue_command (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ GByteArray *buf;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ buf = at_command_to_byte_array (command);
+ g_return_if_fail (buf != NULL);
+
+ mm_serial_port_queue_command (MM_SERIAL_PORT (self),
+ buf,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+void
+mm_at_serial_port_queue_command_cached (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ GByteArray *buf;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ buf = at_command_to_byte_array (command);
+ g_return_if_fail (buf != NULL);
+
+ mm_serial_port_queue_command_cached (MM_SERIAL_PORT (self),
+ buf,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+static void
+debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len)
+{
+ static GString *debug = NULL;
+ const char *s;
+ GTimeVal tv;
+
+ if (!debug)
+ debug = g_string_sized_new (256);
+
+ g_string_append (debug, prefix);
+ g_string_append (debug, " '");
+
+ s = buf;
+ while (len--) {
+ if (g_ascii_isprint (*s))
+ g_string_append_c (debug, *s);
+ else if (*s == '\r')
+ g_string_append (debug, "<CR>");
+ else if (*s == '\n')
+ g_string_append (debug, "<LF>");
+ else
+ g_string_append_printf (debug, "\\%d", *s);
+
+ s++;
+ }
+
+ g_string_append_c (debug, '\'');
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): %s",
+ tv.tv_sec,
+ tv.tv_usec,
+ mm_port_get_device (MM_PORT (port)),
+ debug->str);
+ g_string_truncate (debug, 0);
+}
+
+/*****************************************************************************/
+
+MMAtSerialPort *
+mm_at_serial_port_new (const char *name, MMPortType ptype)
+{
+ return MM_AT_SERIAL_PORT (g_object_new (MM_TYPE_AT_SERIAL_PORT,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
+ MM_PORT_TYPE, ptype,
+ NULL));
+}
+
+static void
+mm_at_serial_port_init (MMAtSerialPort *self)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (object);
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+
+ while (priv->unsolicited_msg_handlers) {
+ MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
+
+ if (handler->notify)
+ handler->notify (handler->user_data);
+
+ g_regex_unref (handler->regex);
+ g_slice_free (MMAtUnsolicitedMsgHandler, 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);
+
+ G_OBJECT_CLASS (mm_at_serial_port_parent_class)->finalize (object);
+}
+
+static void
+mm_at_serial_port_class_init (MMAtSerialPortClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMSerialPortClass *port_class = MM_SERIAL_PORT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMAtSerialPortPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ port_class->parse_unsolicited = parse_unsolicited;
+ port_class->parse_response = parse_response;
+ port_class->handle_response = handle_response;
+ port_class->debug_log = debug_log;
+}
diff --git a/src/mm-at-serial-port.h b/src/mm-at-serial-port.h
new file mode 100644
index 0000000..5d5f13f
--- /dev/null
+++ b/src/mm-at-serial-port.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AT_SERIAL_PORT_H
+#define MM_AT_SERIAL_PORT_H
+
+#include <glib.h>
+#include <glib/gtypes.h>
+#include <glib-object.h>
+
+#include "mm-serial-port.h"
+
+#define MM_TYPE_AT_SERIAL_PORT (mm_at_serial_port_get_type ())
+#define MM_AT_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPort))
+#define MM_AT_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortClass))
+#define MM_IS_AT_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AT_SERIAL_PORT))
+#define MM_IS_AT_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AT_SERIAL_PORT))
+#define MM_AT_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortClass))
+
+typedef struct _MMAtSerialPort MMAtSerialPort;
+typedef struct _MMAtSerialPortClass MMAtSerialPortClass;
+
+typedef gboolean (*MMAtSerialResponseParserFn) (gpointer user_data,
+ GString *response,
+ GError **error);
+
+typedef void (*MMAtSerialUnsolicitedMsgFn) (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data);
+
+typedef void (*MMAtSerialResponseFn) (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+struct _MMAtSerialPort {
+ MMSerialPort parent;
+};
+
+struct _MMAtSerialPortClass {
+ MMSerialPortClass parent;
+};
+
+GType mm_at_serial_port_get_type (void);
+
+MMAtSerialPort *mm_at_serial_port_new (const char *name, MMPortType ptype);
+
+void mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
+ GRegex *regex,
+ MMAtSerialUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void mm_at_serial_port_set_response_parser (MMAtSerialPort *self,
+ MMAtSerialResponseParserFn fn,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void mm_at_serial_port_queue_command (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data);
+
+void mm_at_serial_port_queue_command_cached (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data);
+
+#endif /* MM_AT_SERIAL_PORT_H */
+
diff --git a/src/mm-auth-provider-factory.c b/src/mm-auth-provider-factory.c
new file mode 100644
index 0000000..356dcf2
--- /dev/null
+++ b/src/mm-auth-provider-factory.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "mm-auth-provider.h"
+
+GObject *mm_auth_provider_new (void);
+
+#ifdef WITH_POLKIT
+#define IN_AUTH_PROVIDER_FACTORY_C
+#include "mm-auth-provider-polkit.h"
+#undef IN_AUTH_PROVIDER_FACTORY_C
+#endif
+
+MMAuthProvider *
+mm_auth_provider_get (void)
+{
+ static MMAuthProvider *singleton;
+
+ if (!singleton) {
+#if WITH_POLKIT
+ singleton = (MMAuthProvider *) mm_auth_provider_polkit_new ();
+#else
+ singleton = (MMAuthProvider *) mm_auth_provider_new ();
+#endif
+ }
+
+ g_assert (singleton);
+ return singleton;
+}
+
diff --git a/src/mm-auth-provider-polkit.c b/src/mm-auth-provider-polkit.c
new file mode 100644
index 0000000..c457eaf
--- /dev/null
+++ b/src/mm-auth-provider-polkit.c
@@ -0,0 +1,153 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <polkit/polkit.h>
+
+#include "mm-auth-request-polkit.h"
+#include "mm-auth-provider-polkit.h"
+
+G_DEFINE_TYPE (MMAuthProviderPolkit, mm_auth_provider_polkit, MM_TYPE_AUTH_PROVIDER)
+
+#define MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitPrivate))
+
+typedef struct {
+ PolkitAuthority *authority;
+ guint auth_changed_id;
+} MMAuthProviderPolkitPrivate;
+
+enum {
+ PROP_NAME = 1000,
+};
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_provider_polkit_new (void)
+{
+ return g_object_new (MM_TYPE_AUTH_PROVIDER_POLKIT, NULL);
+}
+
+/*****************************************************************************/
+
+static void
+pk_authority_changed_cb (GObject *object, gpointer user_data)
+{
+ /* Let clients know they should re-check their authorization */
+}
+
+/*****************************************************************************/
+
+static MMAuthRequest *
+real_create_request (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (provider);
+
+ return (MMAuthRequest *) mm_auth_request_polkit_new (priv->authority,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+}
+
+/*****************************************************************************/
+
+static void
+mm_auth_provider_polkit_init (MMAuthProviderPolkit *self)
+{
+ MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (self);
+
+ priv->authority = polkit_authority_get ();
+ if (priv->authority) {
+ priv->auth_changed_id = g_signal_connect (priv->authority,
+ "changed",
+ G_CALLBACK (pk_authority_changed_cb),
+ self);
+ } else
+ g_warning ("%s: failed to create PolicyKit authority.", __func__);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, "polkit");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthProviderPolkit *self = MM_AUTH_PROVIDER_POLKIT (object);
+ MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (self);
+
+ if (priv->auth_changed_id) {
+ g_signal_handler_disconnect (priv->authority, priv->auth_changed_id);
+ priv->auth_changed_id = 0;
+ }
+
+ G_OBJECT_CLASS (mm_auth_provider_polkit_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_provider_polkit_class_init (MMAuthProviderPolkitClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ MMAuthProviderClass *ap_class = MM_AUTH_PROVIDER_CLASS (class);
+
+ mm_auth_provider_polkit_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthProviderPolkitPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ ap_class->create_request = real_create_request;
+
+ /* Properties */
+ g_object_class_override_property (object_class, PROP_NAME, MM_AUTH_PROVIDER_NAME);
+}
+
diff --git a/src/mm-auth-provider-polkit.h b/src/mm-auth-provider-polkit.h
new file mode 100644
index 0000000..a52c56d
--- /dev/null
+++ b/src/mm-auth-provider-polkit.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_PROVIDER_POLKIT_H
+#define MM_AUTH_PROVIDER_POLKIT_H
+
+#include <glib-object.h>
+
+#include "mm-auth-provider.h"
+
+#define MM_TYPE_AUTH_PROVIDER_POLKIT (mm_auth_provider_polkit_get_type ())
+#define MM_AUTH_PROVIDER_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkit))
+#define MM_AUTH_PROVIDER_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitClass))
+#define MM_IS_AUTH_PROVIDER_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT))
+#define MM_IS_AUTH_PROVIDER_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_PROVIDER_POLKIT))
+#define MM_AUTH_PROVIDER_POLKIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitClass))
+
+typedef struct {
+ MMAuthProvider parent;
+} MMAuthProviderPolkit;
+
+typedef struct {
+ MMAuthProviderClass parent;
+} MMAuthProviderPolkitClass;
+
+GType mm_auth_provider_polkit_get_type (void);
+
+GObject *mm_auth_provider_polkit_new (void);
+
+#endif /* MM_AUTH_PROVIDER_POLKIT_H */
+
diff --git a/src/mm-auth-provider.c b/src/mm-auth-provider.c
new file mode 100644
index 0000000..ceff9ad
--- /dev/null
+++ b/src/mm-auth-provider.c
@@ -0,0 +1,300 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "mm-marshal.h"
+#include "mm-auth-provider.h"
+
+GObject *mm_auth_provider_new (void);
+
+G_DEFINE_TYPE (MMAuthProvider, mm_auth_provider, G_TYPE_OBJECT)
+
+#define MM_AUTH_PROVIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_PROVIDER, MMAuthProviderPrivate))
+
+typedef struct {
+ GHashTable *requests;
+ guint process_id;
+} MMAuthProviderPrivate;
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ LAST_PROP
+};
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_provider_new (void)
+{
+ return g_object_new (MM_TYPE_AUTH_PROVIDER, NULL);
+}
+
+/*****************************************************************************/
+
+static void
+remove_requests (MMAuthProvider *self, GSList *remove)
+{
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+ MMAuthRequest *req;
+
+ while (remove) {
+ req = MM_AUTH_REQUEST (remove->data);
+ g_hash_table_remove (priv->requests, req);
+ remove = g_slist_remove (remove, req);
+ }
+}
+
+void
+mm_auth_provider_cancel_request (MMAuthProvider *provider, MMAuthRequest *req)
+{
+ MMAuthProviderPrivate *priv;
+
+ g_return_if_fail (provider != NULL);
+ g_return_if_fail (MM_IS_AUTH_PROVIDER (provider));
+ g_return_if_fail (req != NULL);
+
+ priv = MM_AUTH_PROVIDER_GET_PRIVATE (provider);
+
+ g_return_if_fail (g_hash_table_lookup (priv->requests, req) != NULL);
+ g_hash_table_remove (priv->requests, req);
+}
+
+void
+mm_auth_provider_cancel_for_owner (MMAuthProvider *self, GObject *owner)
+{
+ MMAuthProviderPrivate *priv;
+ GHashTableIter iter;
+ MMAuthRequest *req;
+ gpointer value;
+ GSList *remove = NULL;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_PROVIDER (self));
+
+ /* Find all requests from this owner */
+ priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+ g_hash_table_iter_init (&iter, priv->requests);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ req = MM_AUTH_REQUEST (value);
+ if (mm_auth_request_get_owner (req) == owner)
+ remove = g_slist_prepend (remove, req);
+ }
+
+ /* And cancel/remove them */
+ remove_requests (self, remove);
+}
+
+/*****************************************************************************/
+
+
+static MMAuthRequest *
+real_create_request (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ return (MMAuthRequest *) mm_auth_request_new (0,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+}
+
+static gboolean
+process_complete_requests (gpointer user_data)
+{
+ MMAuthProvider *self = MM_AUTH_PROVIDER (user_data);
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer value;
+ GSList *remove = NULL;
+ MMAuthRequest *req;
+
+ priv->process_id = 0;
+
+ /* Call finished request's callbacks */
+ g_hash_table_iter_init (&iter, priv->requests);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ req = MM_AUTH_REQUEST (value);
+
+ if (mm_auth_request_get_authorization (req) != MM_AUTH_RESULT_UNKNOWN) {
+ mm_auth_request_callback (req);
+ remove = g_slist_prepend (remove, req);
+ }
+ }
+
+ /* And remove those requests from our pending request list */
+ remove_requests (self, remove);
+
+ return FALSE;
+}
+
+static void
+auth_result_cb (MMAuthRequest *req, gpointer user_data)
+{
+ MMAuthProvider *self = MM_AUTH_PROVIDER (user_data);
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+
+ /* Process results from an idle handler */
+ if (priv->process_id == 0)
+ priv->process_id = g_idle_add (process_complete_requests, self);
+}
+
+#define RESULT_SIGID_TAG "result-sigid"
+
+MMAuthRequest *
+mm_auth_provider_request_auth (MMAuthProvider *self,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error)
+{
+ MMAuthProviderPrivate *priv;
+ MMAuthRequest *req;
+ guint32 sigid;
+
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_AUTH_PROVIDER (self), 0);
+ g_return_val_if_fail (authorization != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+
+ req = MM_AUTH_PROVIDER_GET_CLASS (self)->create_request (self,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+ g_assert (req);
+
+ sigid = g_signal_connect (req, "result", G_CALLBACK (auth_result_cb), self);
+ g_object_set_data (G_OBJECT (req), RESULT_SIGID_TAG, GUINT_TO_POINTER (sigid));
+
+ g_hash_table_insert (priv->requests, req, req);
+ if (!mm_auth_request_authenticate (req, error)) {
+ /* Error */
+ g_hash_table_remove (priv->requests, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*****************************************************************************/
+
+static void
+dispose_auth_request (gpointer data)
+{
+ MMAuthRequest *req = MM_AUTH_REQUEST (data);
+ guint sigid;
+
+ sigid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), RESULT_SIGID_TAG));
+ if (sigid)
+ g_signal_handler_disconnect (req, sigid);
+ mm_auth_request_dispose (req);
+ g_object_unref (req);
+}
+
+static void
+mm_auth_provider_init (MMAuthProvider *self)
+{
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+
+ priv->requests = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ dispose_auth_request);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#define NULL_PROVIDER "open"
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, NULL_PROVIDER);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (object);
+
+ if (priv->process_id)
+ g_source_remove (priv->process_id);
+ g_hash_table_destroy (priv->requests);
+
+ G_OBJECT_CLASS (mm_auth_provider_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_provider_class_init (MMAuthProviderClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ mm_auth_provider_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthProviderPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ class->create_request = real_create_request;
+
+ /* Properties */
+ g_object_class_install_property (object_class, PROP_NAME,
+ g_param_spec_string (MM_AUTH_PROVIDER_NAME,
+ "Name",
+ "Provider name",
+ NULL_PROVIDER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
diff --git a/src/mm-auth-provider.h b/src/mm-auth-provider.h
new file mode 100644
index 0000000..26ff340
--- /dev/null
+++ b/src/mm-auth-provider.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_PROVIDER_H
+#define MM_AUTH_PROVIDER_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "mm-auth-request.h"
+
+/* Authorizations */
+#define MM_AUTHORIZATION_DEVICE_INFO "org.freedesktop.ModemManager.Device.Info"
+#define MM_AUTHORIZATION_DEVICE_CONTROL "org.freedesktop.ModemManager.Device.Control"
+#define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager.Contacts"
+#define MM_AUTHORIZATION_SMS "org.freedesktop.ModemManager.SMS"
+#define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager.Location"
+/******************/
+
+
+#define MM_TYPE_AUTH_PROVIDER (mm_auth_provider_get_type ())
+#define MM_AUTH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_PROVIDER, MMAuthProvider))
+#define MM_AUTH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_PROVIDER, MMAuthProviderClass))
+#define MM_IS_AUTH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_PROVIDER))
+#define MM_IS_AUTH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_PROVIDER))
+#define MM_AUTH_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_PROVIDER, MMAuthProviderClass))
+
+#define MM_AUTH_PROVIDER_NAME "name"
+
+typedef struct {
+ GObject parent;
+} MMAuthProvider;
+
+typedef struct {
+ GObjectClass parent;
+
+ MMAuthRequest * (*create_request) (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify);
+} MMAuthProviderClass;
+
+GType mm_auth_provider_get_type (void);
+
+/* Don't do anything clever from the notify callback... */
+MMAuthRequest *mm_auth_provider_request_auth (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error);
+
+void mm_auth_provider_cancel_for_owner (MMAuthProvider *provider,
+ GObject *owner);
+
+/* Subclass API */
+
+/* To get an auth provider instance, implemented in mm-auth-provider-factory.c */
+MMAuthProvider *mm_auth_provider_get (void);
+
+/* schedules the request's completion */
+void mm_auth_provider_finish_request (MMAuthProvider *provider,
+ MMAuthRequest *req,
+ MMAuthResult result);
+
+void mm_auth_provider_cancel_request (MMAuthProvider *provider, MMAuthRequest *req);
+
+#endif /* MM_AUTH_PROVIDER_H */
+
diff --git a/src/mm-auth-request-polkit.c b/src/mm-auth-request-polkit.c
new file mode 100644
index 0000000..2a96bfe
--- /dev/null
+++ b/src/mm-auth-request-polkit.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "mm-auth-request-polkit.h"
+
+G_DEFINE_TYPE (MMAuthRequestPolkit, mm_auth_request_polkit, MM_TYPE_AUTH_REQUEST)
+
+#define MM_AUTH_REQUEST_POLKIT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkitPrivate))
+
+typedef struct {
+ PolkitAuthority *authority;
+ GCancellable *cancellable;
+ PolkitSubject *subject;
+} MMAuthRequestPolkitPrivate;
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_request_polkit_new (PolkitAuthority *authority,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ GObject *obj;
+ MMAuthRequestPolkitPrivate *priv;
+ char *sender;
+
+ g_return_val_if_fail (authorization != NULL, NULL);
+ g_return_val_if_fail (owner != NULL, NULL);
+ g_return_val_if_fail (callback != NULL, NULL);
+ g_return_val_if_fail (context != NULL, NULL);
+
+ obj = mm_auth_request_new (MM_TYPE_AUTH_REQUEST_POLKIT,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+ if (obj) {
+ priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (obj);
+ priv->authority = authority;
+ priv->cancellable = g_cancellable_new ();
+
+ sender = dbus_g_method_get_sender (context);
+ priv->subject = polkit_system_bus_name_new (sender);
+ g_free (sender);
+ }
+
+ return obj;
+}
+
+/*****************************************************************************/
+
+static void
+pk_auth_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ MMAuthRequestPolkit *self = user_data;
+ MMAuthRequestPolkitPrivate *priv;
+ PolkitAuthorizationResult *pk_result;
+ GError *error = NULL;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST_POLKIT (self));
+
+ priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (self);
+ if (!g_cancellable_is_cancelled (priv->cancellable)) {
+ pk_result = polkit_authority_check_authorization_finish (priv->authority,
+ result,
+ &error);
+ if (error) {
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_INTERNAL_FAILURE);
+ g_warning ("%s: PolicyKit authentication error: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ } else if (polkit_authorization_result_get_is_authorized (pk_result))
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_AUTHORIZED);
+ else if (polkit_authorization_result_get_is_challenge (pk_result))
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_CHALLENGE);
+ else
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_NOT_AUTHORIZED);
+
+ g_signal_emit_by_name (self, "result");
+ }
+
+ g_object_unref (self);
+}
+
+static gboolean
+real_authenticate (MMAuthRequest *self, GError **error)
+{
+ MMAuthRequestPolkitPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST_POLKIT (self), FALSE);
+
+ /* We ref ourselves across the polkit call, because we can't get
+ * disposed of while the call is still in-progress, and even if we
+ * cancel ourselves we'll still get the callback.
+ */
+ g_object_ref (self);
+
+ priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (self);
+ polkit_authority_check_authorization (priv->authority,
+ priv->subject,
+ mm_auth_request_get_authorization (MM_AUTH_REQUEST (self)),
+ NULL,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+ priv->cancellable,
+ pk_auth_cb,
+ self);
+ return TRUE;
+}
+
+static void
+real_dispose (MMAuthRequest *req)
+{
+ g_return_if_fail (req != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST_POLKIT (req));
+
+ g_cancellable_cancel (MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (req)->cancellable);
+}
+
+/*****************************************************************************/
+
+static void
+mm_auth_request_polkit_init (MMAuthRequestPolkit *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthRequestPolkitPrivate *priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (object);
+
+ g_object_unref (priv->cancellable);
+ g_object_unref (priv->subject);
+
+ G_OBJECT_CLASS (mm_auth_request_polkit_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_request_polkit_class_init (MMAuthRequestPolkitClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ MMAuthRequestClass *ar_class = MM_AUTH_REQUEST_CLASS (class);
+
+ mm_auth_request_polkit_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthRequestPolkitPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ ar_class->authenticate = real_authenticate;
+ ar_class->dispose = real_dispose;
+}
+
diff --git a/src/mm-auth-request-polkit.h b/src/mm-auth-request-polkit.h
new file mode 100644
index 0000000..384ace8
--- /dev/null
+++ b/src/mm-auth-request-polkit.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_REQUEST_POLKIT_H
+#define MM_AUTH_REQUEST_POLKIT_H
+
+#include <glib-object.h>
+#include <polkit/polkit.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "mm-auth-request.h"
+
+#define MM_TYPE_AUTH_REQUEST_POLKIT (mm_auth_request_polkit_get_type ())
+#define MM_AUTH_REQUEST_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkit))
+#define MM_AUTH_REQUEST_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkitClass))
+#define MM_IS_AUTH_REQUEST_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_REQUEST_POLKIT))
+#define MM_IS_AUTH_REQUEST_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_REQUEST_POLKIT))
+#define MM_AUTH_REQUEST_POLKIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkitClass))
+
+typedef struct {
+ MMAuthRequest parent;
+} MMAuthRequestPolkit;
+
+typedef struct {
+ MMAuthRequestClass parent;
+} MMAuthRequestPolkitClass;
+
+GType mm_auth_request_polkit_get_type (void);
+
+GObject *mm_auth_request_polkit_new (PolkitAuthority *authority,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify);
+
+void mm_auth_request_polkit_cancel (MMAuthRequestPolkit *self);
+
+#endif /* MM_AUTH_REQUEST_POLKIT_H */
+
diff --git a/src/mm-auth-request.c b/src/mm-auth-request.c
new file mode 100644
index 0000000..79f0d42
--- /dev/null
+++ b/src/mm-auth-request.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include "mm-auth-request.h"
+
+G_DEFINE_TYPE (MMAuthRequest, mm_auth_request, G_TYPE_OBJECT)
+
+#define MM_AUTH_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_REQUEST, MMAuthRequestPrivate))
+
+typedef struct {
+ GObject *owner;
+ char *auth;
+ DBusGMethodInvocation *context;
+ MMAuthRequestCb callback;
+ gpointer callback_data;
+
+ MMAuthResult result;
+} MMAuthRequestPrivate;
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_request_new (GType atype,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ GObject *obj;
+ MMAuthRequestPrivate *priv;
+
+ g_return_val_if_fail (authorization != NULL, NULL);
+ g_return_val_if_fail (owner != NULL, NULL);
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ obj = g_object_new (atype ? atype : MM_TYPE_AUTH_REQUEST, NULL);
+ if (obj) {
+ priv = MM_AUTH_REQUEST_GET_PRIVATE (obj);
+ priv->owner = owner; /* not reffed */
+ priv->context = context;
+ priv->auth = g_strdup (authorization);
+ priv->callback = callback;
+ priv->callback_data = callback_data;
+
+ g_object_set_data_full (obj, "caller-data", callback_data, notify);
+ }
+
+ return obj;
+}
+
+/*****************************************************************************/
+
+const char *
+mm_auth_request_get_authorization (MMAuthRequest *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST (self), NULL);
+
+ return MM_AUTH_REQUEST_GET_PRIVATE (self)->auth;
+}
+
+GObject *
+mm_auth_request_get_owner (MMAuthRequest *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST (self), NULL);
+
+ return MM_AUTH_REQUEST_GET_PRIVATE (self)->owner;
+}
+
+MMAuthResult
+mm_auth_request_get_result (MMAuthRequest *self)
+{
+ g_return_val_if_fail (self != NULL, MM_AUTH_RESULT_UNKNOWN);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST (self), MM_AUTH_RESULT_UNKNOWN);
+
+ return MM_AUTH_REQUEST_GET_PRIVATE (self)->result;
+}
+
+void
+mm_auth_request_set_result (MMAuthRequest *self, MMAuthResult result)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST (self));
+ g_return_if_fail (result != MM_AUTH_RESULT_UNKNOWN);
+
+ MM_AUTH_REQUEST_GET_PRIVATE (self)->result = result;
+}
+
+gboolean
+mm_auth_request_authenticate (MMAuthRequest *self, GError **error)
+{
+ return MM_AUTH_REQUEST_GET_CLASS (self)->authenticate (self, error);
+}
+
+void
+mm_auth_request_callback (MMAuthRequest *self)
+{
+ MMAuthRequestPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST (self));
+
+ priv = MM_AUTH_REQUEST_GET_PRIVATE (self);
+ g_warn_if_fail (priv->result != MM_AUTH_RESULT_UNKNOWN);
+
+ if (priv->callback)
+ priv->callback (self, priv->owner, priv->context, priv->callback_data);
+}
+
+void
+mm_auth_request_dispose (MMAuthRequest *self)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST (self));
+
+ if (MM_AUTH_REQUEST_GET_CLASS (self)->dispose)
+ MM_AUTH_REQUEST_GET_CLASS (self)->dispose (self);
+}
+
+/*****************************************************************************/
+
+static gboolean
+real_authenticate (MMAuthRequest *self, GError **error)
+{
+ /* Null auth; everything passes */
+ mm_auth_request_set_result (self, MM_AUTH_RESULT_AUTHORIZED);
+ g_signal_emit_by_name (self, "result");
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+mm_auth_request_init (MMAuthRequest *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthRequestPrivate *priv = MM_AUTH_REQUEST_GET_PRIVATE (object);
+
+ g_free (priv->auth);
+
+ G_OBJECT_CLASS (mm_auth_request_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_request_class_init (MMAuthRequestClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ mm_auth_request_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthRequestPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ class->authenticate = real_authenticate;
+
+ g_signal_new ("result",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+}
+
diff --git a/src/mm-auth-request.h b/src/mm-auth-request.h
new file mode 100644
index 0000000..e22f0a2
--- /dev/null
+++ b/src/mm-auth-request.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_REQUEST_H
+#define MM_AUTH_REQUEST_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define MM_TYPE_AUTH_REQUEST (mm_auth_request_get_type ())
+#define MM_AUTH_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_REQUEST, MMAuthRequest))
+#define MM_AUTH_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_REQUEST, MMAuthRequestClass))
+#define MM_IS_AUTH_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_REQUEST))
+#define MM_IS_AUTH_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_REQUEST))
+#define MM_AUTH_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_REQUEST, MMAuthRequestClass))
+
+typedef enum MMAuthResult {
+ MM_AUTH_RESULT_UNKNOWN = 0,
+ MM_AUTH_RESULT_INTERNAL_FAILURE,
+ MM_AUTH_RESULT_NOT_AUTHORIZED,
+ MM_AUTH_RESULT_CHALLENGE,
+ MM_AUTH_RESULT_AUTHORIZED
+} MMAuthResult;
+
+typedef struct {
+ GObject parent;
+} MMAuthRequest;
+
+typedef struct {
+ GObjectClass parent;
+
+ gboolean (*authenticate) (MMAuthRequest *self, GError **error);
+ void (*dispose) (MMAuthRequest *self);
+} MMAuthRequestClass;
+
+GType mm_auth_request_get_type (void);
+
+typedef void (*MMAuthRequestCb) (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data);
+
+GObject *mm_auth_request_new (GType atype,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify);
+
+const char * mm_auth_request_get_authorization (MMAuthRequest *req);
+GObject * mm_auth_request_get_owner (MMAuthRequest *req);
+MMAuthResult mm_auth_request_get_result (MMAuthRequest *req);
+void mm_auth_request_set_result (MMAuthRequest *req, MMAuthResult result);
+gboolean mm_auth_request_authenticate (MMAuthRequest *req, GError **error);
+void mm_auth_request_callback (MMAuthRequest *req);
+void mm_auth_request_dispose (MMAuthRequest *req);
+
+#endif /* MM_AUTH_REQUEST_H */
+
diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c
index 1882898..1986bb5 100644
--- a/src/mm-callback-info.c
+++ b/src/mm-callback-info.c
@@ -91,6 +91,8 @@ mm_callback_info_schedule (MMCallbackInfo *info)
g_return_if_fail (info->pending_id == 0);
g_return_if_fail (info->called == FALSE);
+ g_warn_if_fail (info->chain_left == 0);
+
info->pending_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, callback_info_do, info, callback_info_done);
}
@@ -208,3 +210,24 @@ mm_callback_info_unref (MMCallbackInfo *info)
}
}
+void
+mm_callback_info_chain_start (MMCallbackInfo *info, guint num)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (num > 0);
+ g_return_if_fail (info->chain_left == 0);
+
+ info->chain_left = num;
+}
+
+void
+mm_callback_info_chain_complete_one (MMCallbackInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->chain_left > 0);
+
+ info->chain_left--;
+ if (info->chain_left == 0)
+ mm_callback_info_schedule (info);
+}
+
diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h
index 66c2048..ef4d65f 100644
--- a/src/mm-callback-info.h
+++ b/src/mm-callback-info.h
@@ -25,6 +25,9 @@ typedef void (*MMCallbackInfoInvokeFn) (MMCallbackInfo *info);
struct _MMCallbackInfo {
guint32 refcount;
+ /* # of ops left in this callback chain */
+ guint32 chain_left;
+
GData *qdata;
MMModem *modem;
@@ -70,5 +73,8 @@ gpointer mm_callback_info_get_data (MMCallbackInfo *info,
MMCallbackInfo *mm_callback_info_ref (MMCallbackInfo *info);
void mm_callback_info_unref (MMCallbackInfo *info);
+void mm_callback_info_chain_start (MMCallbackInfo *info, guint num);
+void mm_callback_info_chain_complete_one (MMCallbackInfo *info);
+
#endif /* MM_CALLBACK_INFO_H */
diff --git a/src/mm-charsets.c b/src/mm-charsets.c
new file mode 100644
index 0000000..c75c3a9
--- /dev/null
+++ b/src/mm-charsets.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-charsets.h"
+#include "mm-utils.h"
+
+typedef struct {
+ const char *gsm_name;
+ const char *other_name;
+ const char *iconv_from_name;
+ const char *iconv_to_name;
+ MMModemCharset charset;
+} CharsetEntry;
+
+static CharsetEntry charset_map[] = {
+ { "UTF-8", "UTF8", "UTF-8", "UTF-8//TRANSLIT", MM_MODEM_CHARSET_UTF8 },
+ { "UCS2", NULL, "UCS-2BE", "UCS-2BE//TRANSLIT", MM_MODEM_CHARSET_UCS2 },
+ { "IRA", "ASCII", "ASCII", "ASCII//TRANSLIT", MM_MODEM_CHARSET_IRA },
+ { "GSM", NULL, NULL, NULL, MM_MODEM_CHARSET_GSM },
+ { "8859-1", NULL, "ISO8859-1", "ISO8859-1//TRANSLIT", MM_MODEM_CHARSET_8859_1 },
+ { "PCCP437", NULL, NULL, NULL, MM_MODEM_CHARSET_PCCP437 },
+ { "PCDN", NULL, NULL, NULL, MM_MODEM_CHARSET_PCDN },
+ { "HEX", NULL, NULL, NULL, MM_MODEM_CHARSET_HEX },
+ { NULL, NULL, NULL, 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->gsm_name) {
+ if (iter->charset == charset)
+ return iter->gsm_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->gsm_name) {
+ if (strcasestr (string, iter->gsm_name))
+ return iter->charset;
+ if (iter->other_name && strcasestr (string, iter->other_name))
+ return iter->charset;
+ iter++;
+ }
+ return MM_MODEM_CHARSET_UNKNOWN;
+}
+
+static const char *
+charset_iconv_to (MMModemCharset charset)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ while (iter->gsm_name) {
+ if (iter->charset == charset)
+ return iter->iconv_to_name;
+ iter++;
+ }
+ g_warn_if_reached ();
+ return NULL;
+}
+
+static const char *
+charset_iconv_from (MMModemCharset charset)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ while (iter->gsm_name) {
+ if (iter->charset == charset)
+ return iter->iconv_from_name;
+ iter++;
+ }
+ g_warn_if_reached ();
+ return NULL;
+}
+
+gboolean
+mm_modem_charset_byte_array_append (GByteArray *array,
+ const char *string,
+ gboolean quoted,
+ MMModemCharset charset)
+{
+ const char *iconv_to;
+ char *converted;
+ GError *error = NULL;
+ gsize written = 0;
+
+ g_return_val_if_fail (array != NULL, FALSE);
+ g_return_val_if_fail (string != NULL, FALSE);
+
+ iconv_to = charset_iconv_to (charset);
+ g_return_val_if_fail (iconv_to != NULL, FALSE);
+
+ converted = g_convert (string,
+ g_utf8_strlen (string, -1),
+ iconv_to,
+ "UTF-8",
+ NULL,
+ &written,
+ &error);
+ if (!converted) {
+ if (error) {
+ g_warning ("%s: failed to convert '%s' to %s character set: (%d) %s",
+ __func__, string, iconv_to,
+ error->code, error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ if (quoted)
+ g_byte_array_append (array, (const guint8 *) "\"", 1);
+ g_byte_array_append (array, (const guint8 *) converted, written);
+ if (quoted)
+ g_byte_array_append (array, (const guint8 *) "\"", 1);
+
+ g_free (converted);
+ return TRUE;
+}
+
+char *
+mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset)
+{
+ char *unconverted;
+ const char *iconv_from;
+ gsize unconverted_len = 0;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ iconv_from = charset_iconv_from (charset);
+ g_return_val_if_fail (iconv_from != NULL, FALSE);
+
+ unconverted = utils_hexstr2bin (src, &unconverted_len);
+ g_return_val_if_fail (unconverted != NULL, NULL);
+
+ if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA)
+ return unconverted;
+
+ return g_convert (unconverted, unconverted_len, "UTF-8//TRANSLIT", iconv_from, NULL, NULL, NULL);
+}
+
diff --git a/src/mm-charsets.h b/src/mm-charsets.h
new file mode 100644
index 0000000..5fa3406
--- /dev/null
+++ b/src/mm-charsets.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_CHARSETS_H
+#define MM_CHARSETS_H
+
+#include <glib.h>
+
+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;
+
+const char *mm_modem_charset_to_string (MMModemCharset charset);
+
+MMModemCharset mm_modem_charset_from_string (const char *string);
+
+/* Append the given string to the given byte array but re-encode it
+ * into the given charset first. The original string is assumed to be
+ * UTF-8 encoded.
+ */
+gboolean mm_modem_charset_byte_array_append (GByteArray *array,
+ const char *string,
+ gboolean quoted,
+ MMModemCharset charset);
+
+/* Take a string in hex representation ("00430052" or "A4BE11" for example)
+ * and convert it from the given character set to UTF-8.
+ */
+char *mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset);
+
+#endif /* MM_CHARSETS_H */
+
diff --git a/src/mm-errors.c b/src/mm-errors.c
index 34f56c1..e4fdda7 100644
--- a/src/mm-errors.c
+++ b/src/mm-errors.c
@@ -16,6 +16,9 @@
#include "mm-errors.h"
+#include <string.h>
+#include <ctype.h>
+
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
GQuark
@@ -36,10 +39,12 @@ mm_serial_error_get_type (void)
if (etype == 0) {
static const GEnumValue values[] = {
- ENUM_ENTRY (MM_SERIAL_OPEN_FAILED, "SerialOpenFailed"),
- ENUM_ENTRY (MM_SERIAL_SEND_FAILED, "SerialSendfailed"),
- ENUM_ENTRY (MM_SERIAL_RESPONSE_TIMEOUT, "SerialResponseTimeout"),
- ENUM_ENTRY (MM_SERIAL_OPEN_FAILED_NO_DEVICE, "SerialOpenFailedNoDevice"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_OPEN_FAILED, "SerialOpenFailed"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_SEND_FAILED, "SerialSendfailed"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_RESPONSE_TIMEOUT, "SerialResponseTimeout"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE, "SerialOpenFailedNoDevice"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_FLASH_FAILED, "SerialFlashFailed"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_NOT_OPEN, "SerialNotOpen"),
{ 0, 0, 0 }
};
@@ -73,6 +78,8 @@ mm_modem_error_get_type (void)
ENUM_ENTRY (MM_MODEM_ERROR_DISCONNECTED, "Disconnected"),
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 }
};
@@ -222,69 +229,127 @@ mm_mobile_error_get_type (void)
return etype;
}
+typedef struct {
+ int code;
+ const char *error; /* lowercase, and stripped of special chars and whitespace */
+ const char *message;
+} ErrorTable;
+
+static ErrorTable errors[] = {
+ { MM_MOBILE_ERROR_PHONE_FAILURE, "phonefailure", "Phone failure" },
+ { MM_MOBILE_ERROR_NO_CONNECTION, "noconnectiontophone", "No connection to phone" },
+ { MM_MOBILE_ERROR_LINK_RESERVED, "phoneadapterlinkreserved", "Phone-adaptor link reserved" },
+ { MM_MOBILE_ERROR_NOT_ALLOWED, "operationnotallowed", "Operation not allowed" },
+ { MM_MOBILE_ERROR_NOT_SUPPORTED, "operationnotsupported", "Operation not supported" },
+ { MM_MOBILE_ERROR_PH_SIM_PIN, "phsimpinrequired", "PH-SIM PIN required" },
+ { MM_MOBILE_ERROR_PH_FSIM_PIN, "phfsimpinrequired", "PH-FSIM PIN required" },
+ { MM_MOBILE_ERROR_PH_FSIM_PUK, "phfsimpukrequired", "PH-FSIM PUK required" },
+ { MM_MOBILE_ERROR_SIM_NOT_INSERTED, "simnotinserted", "SIM not inserted" },
+ { MM_MOBILE_ERROR_SIM_PIN, "simpinrequired", "SIM PIN required" },
+ { MM_MOBILE_ERROR_SIM_PUK, "simpukrequired", "SIM PUK required" },
+ { MM_MOBILE_ERROR_SIM_FAILURE, "simfailure", "SIM failure" },
+ { MM_MOBILE_ERROR_SIM_BUSY, "simbusy", "SIM busy" },
+ { MM_MOBILE_ERROR_SIM_WRONG, "simwrong", "SIM wrong" },
+ { MM_MOBILE_ERROR_WRONG_PASSWORD, "incorrectpassword", "Incorrect password" },
+ { MM_MOBILE_ERROR_SIM_PIN2, "simpin2required", "SIM PIN2 required" },
+ { MM_MOBILE_ERROR_SIM_PUK2, "simpuk2required", "SIM PUK2 required" },
+ { MM_MOBILE_ERROR_MEMORY_FULL, "memoryfull", "Memory full" },
+ { MM_MOBILE_ERROR_INVALID_INDEX, "invalidindex", "Invalid index" },
+ { MM_MOBILE_ERROR_NOT_FOUND, "notfound", "Not found" },
+ { MM_MOBILE_ERROR_MEMORY_FAILURE, "memoryfailure", "Memory failure" },
+ { MM_MOBILE_ERROR_TEXT_TOO_LONG, "textstringtoolong", "Text string too long" },
+ { MM_MOBILE_ERROR_INVALID_CHARS, "invalidcharactersintextstring", "Invalid characters in text string" },
+ { MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG, "dialstringtoolong", "Dial string too long" },
+ { MM_MOBILE_ERROR_DIAL_STRING_INVALID, "invalidcharactersindialstring", "Invalid characters in dial string" },
+ { MM_MOBILE_ERROR_NO_NETWORK, "nonetworkservice", "No network service" },
+ { MM_MOBILE_ERROR_NETWORK_TIMEOUT, "networktimeout", "Network timeout" },
+ { MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED, "networknotallowedemergencycallsonly", "Network not allowed - emergency calls only" },
+ { MM_MOBILE_ERROR_NETWORK_PIN, "networkpersonalizationpinrequired", "Network personalization PIN required" },
+ { MM_MOBILE_ERROR_NETWORK_PUK, "networkpersonalizationpukrequired", "Network personalization PUK required" },
+ { MM_MOBILE_ERROR_NETWORK_SUBSET_PIN, "networksubsetpersonalizationpinrequired", "Network subset personalization PIN required" },
+ { MM_MOBILE_ERROR_NETWORK_SUBSET_PUK, "networksubsetpersonalizationpukrequired", "Network subset personalization PUK required" },
+ { MM_MOBILE_ERROR_SERVICE_PIN, "serviceproviderpersonalizationpinrequired", "Service provider personalization PIN required" },
+ { MM_MOBILE_ERROR_SERVICE_PUK, "serviceproviderpersonalizationpukrequired", "Service provider personalization PUK required" },
+ { MM_MOBILE_ERROR_CORP_PIN, "corporatepersonalizationpinrequired", "Corporate personalization PIN required" },
+ { MM_MOBILE_ERROR_CORP_PUK, "corporatepersonalizationpukrequired", "Corporate personalization PUK required" },
+ { MM_MOBILE_ERROR_HIDDEN_KEY, "phsimpukrequired", "Hidden key required" },
+ { MM_MOBILE_ERROR_EAP_NOT_SUPPORTED, "eapmethodnotsupported", "EAP method not supported" },
+ { MM_MOBILE_ERROR_INCORRECT_PARAMS, "incorrectparameters", "Incorrect parameters" },
+ { MM_MOBILE_ERROR_UNKNOWN, "unknownerror", "Unknown error" },
+ { MM_MOBILE_ERROR_GPRS_ILLEGAL_MS, "illegalms", "Illegal MS" },
+ { MM_MOBILE_ERROR_GPRS_ILLEGAL_ME, "illegalme", "Illegal ME" },
+ { MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED, "gprsservicesnotallowed", "GPRS services not allowed" },
+ { MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED, "plmnnotallowed", "PLMN not allowed" },
+ { MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED, "locationareanotallowed", "Location area not allowed" },
+ { MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED, "roamingnotallowedinthislocationarea", "Roaming not allowed in this location area" },
+ { MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED, "serviceoperationnotsupported", "Service option not supported" },
+ { MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED, "requestedserviceoptionnotsubscribed", "Requested service option not subscribed" },
+ { MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER, "serviceoptiontemporarilyoutoforder", "Service option temporarily out of order" },
+ { MM_MOBILE_ERROR_GPRS_UNKNOWN, "unspecifiedgprserror", "Unspecified GPRS error" },
+ { MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE, "pdpauthenticationfailure", "PDP authentication failure" },
+ { MM_MOBILE_ERROR_GPRS_INVALID_CLASS, "invalidmobileclass", "Invalid mobile class" },
+ { -1, NULL, NULL }
+};
+
GError *
mm_mobile_error_for_code (int error_code)
{
- const char *msg;
+ const char *msg = NULL;
+ const ErrorTable *ptr = &errors[0];
+
+ while (ptr->code >= 0) {
+ if (ptr->code == error_code) {
+ msg = ptr->message;
+ break;
+ }
+ ptr++;
+ }
- switch (error_code) {
- case MM_MOBILE_ERROR_PHONE_FAILURE: msg = "Phone failure"; break;
- case MM_MOBILE_ERROR_NO_CONNECTION: msg = "No connection to phone"; break;
- case MM_MOBILE_ERROR_LINK_RESERVED: msg = "Phone-adaptor link reserved"; break;
- case MM_MOBILE_ERROR_NOT_ALLOWED: msg = "Operation not allowed"; break;
- case MM_MOBILE_ERROR_NOT_SUPPORTED: msg = "Operation not supported"; break;
- case MM_MOBILE_ERROR_PH_SIM_PIN: msg = "PH-SIM PIN required"; break;
- case MM_MOBILE_ERROR_PH_FSIM_PIN: msg = "PH-FSIM PIN required"; break;
- case MM_MOBILE_ERROR_PH_FSIM_PUK: msg = "PH-FSIM PUK required"; break;
- case MM_MOBILE_ERROR_SIM_NOT_INSERTED: msg = "SIM not inserted"; break;
- case MM_MOBILE_ERROR_SIM_PIN: msg = "SIM PIN required"; break;
- case MM_MOBILE_ERROR_SIM_PUK: msg = "SIM PUK required"; break;
- case MM_MOBILE_ERROR_SIM_FAILURE: msg = "SIM failure"; break;
- case MM_MOBILE_ERROR_SIM_BUSY: msg = "SIM busy"; break;
- case MM_MOBILE_ERROR_SIM_WRONG: msg = "SIM wrong"; break;
- case MM_MOBILE_ERROR_WRONG_PASSWORD: msg = "Incorrect password"; break;
- case MM_MOBILE_ERROR_SIM_PIN2: msg = "SIM PIN2 required"; break;
- case MM_MOBILE_ERROR_SIM_PUK2: msg = "SIM PUK2 required"; break;
- case MM_MOBILE_ERROR_MEMORY_FULL: msg = "Memory full"; break;
- case MM_MOBILE_ERROR_INVALID_INDEX: msg = "Invalid index"; break;
- case MM_MOBILE_ERROR_NOT_FOUND: msg = "Not found"; break;
- case MM_MOBILE_ERROR_MEMORY_FAILURE: msg = "Memory failure"; break;
- case MM_MOBILE_ERROR_TEXT_TOO_LONG: msg = "Text string too long"; break;
- case MM_MOBILE_ERROR_INVALID_CHARS: msg = "Invalid characters in text string"; break;
- case MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG: msg = "Dial string too long"; break;
- case MM_MOBILE_ERROR_DIAL_STRING_INVALID: msg = "Invalid characters in dial string"; break;
- case MM_MOBILE_ERROR_NO_NETWORK: msg = "No network service"; break;
- case MM_MOBILE_ERROR_NETWORK_TIMEOUT: msg = "Network timeout"; break;
- case MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED: msg = "Network not allowed - emergency calls only"; break;
- case MM_MOBILE_ERROR_NETWORK_PIN: msg = "Network personalization PIN required"; break;
- case MM_MOBILE_ERROR_NETWORK_PUK: msg = "Network personalization PUK required"; break;
- case MM_MOBILE_ERROR_NETWORK_SUBSET_PIN: msg = "Network subset personalization PIN required"; break;
- case MM_MOBILE_ERROR_NETWORK_SUBSET_PUK: msg = "Network subset personalization PUK required"; break;
- case MM_MOBILE_ERROR_SERVICE_PIN: msg = "Service provider personalization PIN required"; break;
- case MM_MOBILE_ERROR_SERVICE_PUK: msg = "Service provider personalization PUK required"; break;
- case MM_MOBILE_ERROR_CORP_PIN: msg = "Corporate personalization PIN required"; break;
- case MM_MOBILE_ERROR_CORP_PUK: msg = "Corporate personalization PUK required"; break;
- case MM_MOBILE_ERROR_HIDDEN_KEY: msg = "Hidden key required"; break;
- case MM_MOBILE_ERROR_EAP_NOT_SUPPORTED: msg = "EAP method not supported"; break;
- case MM_MOBILE_ERROR_INCORRECT_PARAMS: msg = "Incorrect parameters"; break;
- case MM_MOBILE_ERROR_UNKNOWN: msg = "Unknown error"; break;
- case MM_MOBILE_ERROR_GPRS_ILLEGAL_MS: msg = "Illegal MS"; break;
- case MM_MOBILE_ERROR_GPRS_ILLEGAL_ME: msg = "Illegal ME"; break;
- case MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED: msg = "GPRS services not allowed"; break;
- case MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED: msg = "PLMN not allowed"; break;
- case MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED: msg = "Location area not allowed"; break;
- case MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED: msg = "Roaming not allowed in this location area"; break;
- case MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED: msg = "Service option not supported"; break;
- case MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED: msg = "Requested service option not subscribed"; break;
- case MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER: msg = "Service option temporarily out of order"; break;
- case MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE: msg = "PDP authentication failure"; break;
- case MM_MOBILE_ERROR_GPRS_UNKNOWN: msg = "Unspecified GPRS error"; break;
- case MM_MOBILE_ERROR_GPRS_INVALID_CLASS: msg = "Invalid mobile class"; break;
- default:
- g_warning ("Invalid error code");
+ if (!msg) {
+ g_warning ("Invalid error code: %d", error_code);
error_code = MM_MOBILE_ERROR_UNKNOWN;
msg = "Unknown error";
}
return g_error_new_literal (MM_MOBILE_ERROR, error_code, msg);
}
+
+#define BUF_SIZE 100
+
+GError *
+mm_mobile_error_for_string (const char *str)
+{
+ int error_code = -1;
+ const ErrorTable *ptr = &errors[0];
+ char buf[BUF_SIZE + 1];
+ const char *msg = NULL, *p = str;
+ int i = 0;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ /* Normalize the error code by stripping whitespace and odd characters */
+ while (*p && i < BUF_SIZE) {
+ if (isalnum (*p))
+ buf[i++] = tolower (*p);
+ p++;
+ }
+ buf[i] = '\0';
+
+ while (ptr->code >= 0) {
+ if (!strcmp (buf, ptr->error)) {
+ error_code = ptr->code;
+ msg = ptr->message;
+ break;
+ }
+ ptr++;
+ }
+
+ if (!msg) {
+ g_warning ("Invalid error code: %d", error_code);
+ error_code = MM_MOBILE_ERROR_UNKNOWN;
+ msg = "Unknown error";
+ }
+
+ return g_error_new_literal (MM_MOBILE_ERROR, error_code, msg);
+}
+
diff --git a/src/mm-errors.h b/src/mm-errors.h
index c02a351..13a531d 100644
--- a/src/mm-errors.h
+++ b/src/mm-errors.h
@@ -20,10 +20,12 @@
#include <glib-object.h>
enum {
- MM_SERIAL_OPEN_FAILED = 0,
- MM_SERIAL_SEND_FAILED = 1,
- MM_SERIAL_RESPONSE_TIMEOUT = 2,
- MM_SERIAL_OPEN_FAILED_NO_DEVICE = 3
+ MM_SERIAL_ERROR_OPEN_FAILED = 0,
+ MM_SERIAL_ERROR_SEND_FAILED = 1,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT = 2,
+ MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE = 3,
+ MM_SERIAL_ERROR_FLASH_FAILED = 4,
+ MM_SERIAL_ERROR_NOT_OPEN = 5,
};
#define MM_SERIAL_ERROR (mm_serial_error_quark ())
@@ -39,7 +41,9 @@ enum {
MM_MODEM_ERROR_CONNECTED = 2,
MM_MODEM_ERROR_DISCONNECTED = 3,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS = 4,
- MM_MODEM_ERROR_REMOVED = 5
+ MM_MODEM_ERROR_REMOVED = 5,
+ MM_MODEM_ERROR_AUTHORIZATION_REQUIRED = 6,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET = 7
};
#define MM_MODEM_ERROR (mm_modem_error_quark ())
@@ -64,6 +68,7 @@ GType mm_modem_connect_error_get_type (void);
GError *mm_modem_connect_error_for_code (int error_code);
+/* 3GPP TS 07.07 version 7.8.0 Release 1998 (page 90) ETSI TS 100 916 V7.8.0 (2003-03) */
enum {
MM_MOBILE_ERROR_PHONE_FAILURE = 0,
MM_MOBILE_ERROR_NO_CONNECTION = 1,
@@ -115,8 +120,8 @@ enum {
MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED = 132,
MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED = 133,
MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER = 134,
- MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149,
MM_MOBILE_ERROR_GPRS_UNKNOWN = 148,
+ MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149,
MM_MOBILE_ERROR_GPRS_INVALID_CLASS = 150
};
@@ -127,5 +132,6 @@ enum {
GQuark mm_mobile_error_quark (void);
GType mm_mobile_error_get_type (void);
GError *mm_mobile_error_for_code (int error_code);
+GError *mm_mobile_error_for_string (const char *str);
#endif /* MM_MODEM_ERROR_H */
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
index 50cd86c..9fe897f 100644
--- a/src/mm-generic-cdma.c
+++ b/src/mm-generic-cdma.c
@@ -23,10 +23,15 @@
#include "mm-generic-cdma.h"
#include "mm-modem-cdma.h"
#include "mm-modem-simple.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-serial-parsers.h"
+#include "mm-modem-helpers.h"
+#include "libqcdm/src/commands.h"
+
+#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state"
static void simple_reg_callback (MMModemCdma *modem,
MMModemCdmaRegistrationState cdma_1x_reg_state,
@@ -58,6 +63,10 @@ typedef struct {
gboolean evdo_rev0;
gboolean evdo_revA;
gboolean reg_try_css;
+ gboolean has_spservice;
+ gboolean has_speri;
+
+ guint poll_id;
MMModemCdmaRegistrationState cdma_1x_reg_state;
MMModemCdmaRegistrationState evdo_reg_state;
@@ -67,8 +76,9 @@ typedef struct {
guint reg_state_changed_id;
MMCallbackInfo *simple_connect_info;
- MMSerialPort *primary;
- MMSerialPort *secondary;
+ MMAtSerialPort *primary;
+ MMAtSerialPort *secondary;
+ MMQcdmSerialPort *qcdm;
MMPort *data;
} MMGenericCdmaPrivate;
@@ -114,6 +124,44 @@ check_valid (MMGenericCdma *self)
mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid);
}
+static void
+get_esn_cb (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_modem_base_set_equipment_identifier (MM_MODEM_BASE (modem), error ? "" : result);
+ mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_CDMA_GET_PRIVATE (modem)->primary));
+ check_valid (MM_GENERIC_CDMA (modem));
+ }
+}
+
+static void
+initial_esn_check (MMGenericCdma *self)
+{
+ GError *error = NULL;
+ MMGenericCdmaPrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_CDMA (self));
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ /* Make sure echoing is off */
+ mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL);
+ mm_modem_cdma_get_esn (MM_MODEM_CDMA (self), get_esn_cb, NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ check_valid (self);
+ }
+}
+
static gboolean
owns_port (MMModem *modem, const char *subsys, const char *name)
{
@@ -148,25 +196,36 @@ mm_generic_cdma_grab_port (MMGenericCdma *self,
}
port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
- if (port && MM_IS_SERIAL_PORT (port)) {
+ if (!port) {
+ g_warn_if_fail (port != NULL);
+ return NULL;
+ }
+
+ if (MM_IS_AT_SERIAL_PORT (port)) {
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
- mm_serial_port_set_response_parser (MM_SERIAL_PORT (port),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
+ mm_serial_parser_v1_e1_parse,
+ mm_serial_parser_v1_e1_new (),
+ mm_serial_parser_v1_e1_destroy);
if (ptype == MM_PORT_TYPE_PRIMARY) {
- priv->primary = MM_SERIAL_PORT (port);
+ priv->primary = MM_AT_SERIAL_PORT (port);
if (!priv->data) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
}
- check_valid (self);
+
+ /* Get modem's ESN number */
+ initial_esn_check (self);
+
} else if (ptype == MM_PORT_TYPE_SECONDARY)
- priv->secondary = MM_SERIAL_PORT (port);
- } else {
+ priv->secondary = MM_AT_SERIAL_PORT (port);
+ } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
+ if (!priv->qcdm)
+ priv->qcdm = MM_QCDM_SERIAL_PORT (port);
+ } else if (!strcmp (subsys, "net")) {
/* Net device (if any) is the preferred data port */
- if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) {
+ if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
check_valid (self);
@@ -197,7 +256,7 @@ release_port (MMModem *modem, const char *subsys, const char *name)
if (!port)
return;
- if (port == MM_PORT (priv->primary)) {
+ if (port == (MMPort *) priv->primary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->primary = NULL;
}
@@ -207,17 +266,22 @@ release_port (MMModem *modem, const char *subsys, const char *name)
g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE);
}
- if (port == MM_PORT (priv->secondary)) {
+ if (port == (MMPort *) priv->secondary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->secondary = NULL;
}
+ if (port == (MMPort *) priv->qcdm) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->qcdm = NULL;
+ }
+
check_valid (MM_GENERIC_CDMA (modem));
}
-MMSerialPort *
-mm_generic_cdma_get_port (MMGenericCdma *modem,
- MMPortType ptype)
+MMAtSerialPort *
+mm_generic_cdma_get_at_port (MMGenericCdma *modem,
+ MMPortType ptype)
{
g_return_val_if_fail (MM_IS_GENERIC_CDMA (modem), NULL);
g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
@@ -230,6 +294,27 @@ mm_generic_cdma_get_port (MMGenericCdma *modem,
return NULL;
}
+MMAtSerialPort *
+mm_generic_cdma_get_best_at_port (MMGenericCdma *self, GError **error)
+{
+ MMGenericCdmaPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), NULL);
+
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
+ if (!mm_port_get_connected (MM_PORT (priv->primary)))
+ return priv->primary;
+
+ if (!priv->secondary) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot perform this operation while connected");
+ }
+
+ return priv->secondary;
+}
+
/*****************************************************************************/
void
@@ -301,6 +386,38 @@ mm_generic_cdma_evdo_get_registration_state_sync (MMGenericCdma *self)
/*****************************************************************************/
static void
+periodic_poll_reg_cb (MMModemCdma *modem,
+ MMModemCdmaRegistrationState cdma_1x_reg_state,
+ MMModemCdmaRegistrationState evdo_reg_state,
+ GError *error,
+ gpointer user_data)
+{
+ /* cached reg state already updated */
+}
+
+static void
+periodic_poll_signal_quality_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ /* cached signal quality already updated */
+}
+
+static gboolean
+periodic_poll_cb (gpointer user_data)
+{
+ MMGenericCdma *self = MM_GENERIC_CDMA (user_data);
+
+ mm_modem_cdma_get_registration_state (MM_MODEM_CDMA (self), periodic_poll_reg_cb, NULL);
+ mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (self), periodic_poll_signal_quality_cb, NULL);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
update_enabled_state (MMGenericCdma *self,
gboolean stay_connected,
MMModemStateReason reason)
@@ -349,6 +466,43 @@ registration_cleanup (MMGenericCdma *self, GQuark error_class, guint32 error_num
}
static void
+get_enable_info_done (MMModem *modem,
+ const char *manufacturer,
+ const char *model,
+ const char *version,
+ GError *error,
+ gpointer user_data)
+{
+ /* Modem base class handles the response for us */
+}
+
+static void
+spservice_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error) {
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->has_spservice = TRUE;
+
+ /* +SPSERVICE provides a better indicator of registration status than
+ * +CSS, which some devices implement inconsistently.
+ */
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->reg_try_css = FALSE;
+ }
+}
+
+static void
+speri_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error)
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->has_speri = TRUE;
+}
+
+static void
enable_all_done (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = user_data;
@@ -358,15 +512,33 @@ enable_all_done (MMModem *modem, GError *error, gpointer user_data)
if (error)
info->error = g_error_copy (error);
else {
+ /* Try to enable XON/XOFF flow control */
+ mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL);
+
/* Open up the second port, if one exists */
if (priv->secondary) {
- if (!mm_serial_port_open (priv->secondary, &info->error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &info->error)) {
+ g_assert (info->error);
+ goto out;
+ }
+ }
+
+ /* Open up the second port, if one exists */
+ if (priv->qcdm) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->qcdm), &info->error)) {
g_assert (info->error);
goto out;
}
}
update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
+
+ /* Grab device info right away */
+ mm_modem_get_info (modem, get_enable_info_done, NULL);
+
+ /* Check for support of Sprint-specific phone commands */
+ mm_at_serial_port_queue_command (priv->primary, "+SPSERVICE?", 3, spservice_done, self);
+ mm_at_serial_port_queue_command (priv->primary, "$SPERI?", 3, speri_done, self);
}
out:
@@ -380,7 +552,7 @@ out:
}
static void
-enable_error_reporting_done (MMSerialPort *port,
+enable_error_reporting_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -399,7 +571,7 @@ enable_error_reporting_done (MMSerialPort *port,
}
static void
-init_done (MMSerialPort *port,
+init_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -419,7 +591,7 @@ init_done (MMSerialPort *port,
FIXME: It's mandatory by spec, so it really shouldn't be optional. Figure
out which CDMA modems have problems with it and implement plugin for them.
*/
- mm_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data);
+ mm_at_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data);
}
}
@@ -439,7 +611,7 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data)
return;
}
- mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "Z E0 V1 X4 &C1", 3, init_done, user_data);
}
static void
@@ -453,7 +625,7 @@ enable (MMModem *modem,
info = mm_callback_info_new (modem, callback, user_data);
- if (!mm_serial_port_open (priv->primary, &info->error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &info->error)) {
g_assert (info->error);
mm_callback_info_schedule (info);
return;
@@ -463,7 +635,7 @@ enable (MMModem *modem,
MM_MODEM_STATE_ENABLING,
MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (priv->primary, 100, flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 100, FALSE, flash_done, info);
}
static void
@@ -489,7 +661,7 @@ disable_all_done (MMModem *modem, GError *error, gpointer user_data)
MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- mm_serial_port_close (priv->primary);
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary));
mm_modem_set_state (modem, MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_REASON_NONE);
priv->cdma_1x_reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
@@ -545,21 +717,24 @@ disable (MMModem *modem,
GUINT_TO_POINTER (state),
NULL);
+ /* Close auxiliary serial ports */
if (priv->secondary)
- mm_serial_port_close (priv->secondary);
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary));
+ if (priv->qcdm)
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->qcdm));
mm_modem_set_state (MM_MODEM (info->modem),
MM_MODEM_STATE_DISABLING,
MM_MODEM_STATE_REASON_NONE);
if (mm_port_get_connected (MM_PORT (priv->primary)))
- mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disable_flash_done, info);
else
- disable_flash_done (priv->primary, NULL, info);
+ disable_flash_done (MM_SERIAL_PORT (priv->primary), NULL, info);
}
static void
-dial_done (MMSerialPort *port,
+dial_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -598,7 +773,7 @@ connect (MMModem *modem,
info = mm_callback_info_new (modem, callback, user_data);
command = g_strconcat ("DT", number, NULL);
- mm_serial_port_queue_command (priv->primary, command, 90, dial_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 90, dial_done, info);
g_free (command);
}
@@ -648,83 +823,7 @@ disconnect (MMModem *modem,
NULL);
mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info);
-}
-
-static void
-card_info_invoke (MMCallbackInfo *info)
-{
- MMModemInfoFn callback = (MMModemInfoFn) info->callback;
-
- callback (info->modem,
- (char *) mm_callback_info_get_data (info, "card-info-manufacturer"),
- (char *) mm_callback_info_get_data (info, "card-info-model"),
- (char *) mm_callback_info_get_data (info, "card-info-version"),
- info->error, info->user_data);
-}
-
-static const char *
-strip_response (const char *resp, const char *cmd)
-{
- const char *p = resp;
-
- if (p) {
- if (!strncmp (p, cmd, strlen (cmd)))
- p += strlen (cmd);
- while (*p == ' ')
- p++;
- }
- return p;
-}
-
-static void
-get_version_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *p;
-
- if (!error) {
- p = strip_response (response->str, "+GMR:");
- mm_callback_info_set_data (info, "card-info-version", g_strdup (p), g_free);
- } else if (!info->error)
- info->error = g_error_copy (error);
-
- mm_callback_info_schedule (info);
-}
-
-static void
-get_model_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *p;
-
- if (!error) {
- p = strip_response (response->str, "+GMM:");
- mm_callback_info_set_data (info, "card-info-model", g_strdup (p), g_free);
- } else if (!info->error)
- info->error = g_error_copy (error);
-}
-
-static void
-get_manufacturer_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *p;
-
- if (!error) {
- p = strip_response (response->str, "+GMI:");
- mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (p), g_free);
- } else
- info->error = g_error_copy (error);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
}
static void
@@ -732,30 +831,12 @@ get_card_info (MMModem *modem,
MMModemInfoFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
- MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
-
- info = mm_callback_info_new_full (MM_MODEM (modem),
- card_info_invoke,
- G_CALLBACK (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 modem info while connected");
- mm_callback_info_schedule (info);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
- }
+ MMAtSerialPort *port;
+ GError *error = NULL;
- mm_serial_port_queue_command_cached (port, "+GMI", 3, get_manufacturer_done, info);
- mm_serial_port_queue_command_cached (port, "+GMM", 3, get_model_done, info);
- mm_serial_port_queue_command_cached (port, "+GMR", 3, get_version_done, info);
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &error);
+ mm_modem_base_get_card_info (MM_MODEM_BASE (modem), port, error, callback, user_data);
+ g_clear_error (&error);
}
/*****************************************************************************/
@@ -793,7 +874,7 @@ mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality)
#define CSQ2_TRIED "csq?-tried"
static void
-get_signal_quality_done (MMSerialPort *port,
+get_signal_quality_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -811,7 +892,7 @@ get_signal_quality_done (MMSerialPort *port,
* try the other command if the first one fails.
*/
mm_callback_info_set_data (info, CSQ2_TRIED, GUINT_TO_POINTER (1), NULL);
- mm_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info);
+ mm_at_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info);
return;
}
} else {
@@ -847,31 +928,102 @@ get_signal_quality_done (MMSerialPort *port,
}
static void
+qcdm_pilot_sets_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericCdmaPrivate *priv;
+ QCDMResult *result;
+ guint32 num = 0, quality = 0, i;
+ float best_db = -28;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+
+ /* Parse the response */
+ result = qcdm_cmd_pilot_sets_result ((const char *) response->data, response->len, &info->error);
+ if (!result)
+ goto done;
+
+ qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, &num);
+ for (i = 0; i < num; i++) {
+ guint32 pn_offset = 0, ecio = 0;
+ float db = 0;
+
+ qcdm_cmd_pilot_sets_result_get_pilot (result,
+ QCDM_CMD_PILOT_SETS_TYPE_ACTIVE,
+ i,
+ &pn_offset,
+ &ecio,
+ &db);
+ best_db = MAX (db, best_db);
+ }
+ qcdm_result_unref (result);
+
+ if (num > 0) {
+ #define BEST_ECIO 3
+ #define WORST_ECIO 25
+
+ /* EC/IO dB ranges from roughly 0 to -31 dB. Lower == worse. We
+ * really only care about -3 to -25 dB though, since that's about what
+ * you'll see in real-world usage.
+ */
+ best_db = CLAMP (ABS (best_db), BEST_ECIO, WORST_ECIO) - BEST_ECIO;
+ quality = (guint32) (100 - (best_db * 100 / (WORST_ECIO - BEST_ECIO)));
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
+
+ if (priv->cdma1x_quality != quality) {
+ priv->cdma1x_quality = quality;
+ mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (info->modem), quality);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
get_signal_quality (MMModemCdma *modem,
MMModemUIntFn callback,
gpointer user_data)
{
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
+ MMAtSerialPort *at_port;
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- g_message ("Returning saved signal quality %d", priv->cdma1x_quality);
- callback (MM_MODEM (modem), priv->cdma1x_quality, NULL, user_data);
- return;
- }
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- /* Use secondary port if primary is connected */
- port = priv->secondary;
+ at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error);
+ if (!at_port && !priv->qcdm) {
+ g_message ("Returning saved signal quality %d", priv->cdma1x_quality);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->cdma1x_quality), NULL);
+ mm_callback_info_schedule (info);
+ return;
+ }
+ g_clear_error (&info->error);
+
+ if (at_port)
+ mm_at_serial_port_queue_command (at_port, "+CSQ", 3, get_signal_quality_done, info);
+ else if (priv->qcdm) {
+ GByteArray *pilot_sets;
+
+ /* Use CDMA1x pilot EC/IO if we can */
+ pilot_sets = g_byte_array_sized_new (25);
+ pilot_sets->len = qcdm_cmd_pilot_sets_new ((char *) pilot_sets->data, 25, NULL);
+ g_assert (pilot_sets->len);
+ mm_qcdm_serial_port_queue_command (priv->qcdm, pilot_sets, 3, qcdm_pilot_sets_cb, info);
}
-
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command (port, "+CSQ", 3, get_signal_quality_done, info);
}
static void
-get_string_done (MMSerialPort *port,
+get_string_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -882,7 +1034,7 @@ get_string_done (MMSerialPort *port,
if (error)
info->error = g_error_copy (error);
else {
- p = strip_response (response->str, "+GSN:");
+ p = mm_strip_tag (response->str, "+GSN:");
mm_callback_info_set_result (info, g_strdup (p), g_free);
}
@@ -894,26 +1046,18 @@ get_esn (MMModemCdma *modem,
MMModemStringFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
- GError *error;
- MMSerialPort *port = priv->primary;
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get ESN while connected");
- callback (MM_MODEM (modem), NULL, error, user_data);
- g_error_free (error);
- return;
- }
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- /* Use secondary port if primary is connected */
- port = priv->secondary;
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
- info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info);
}
static void
@@ -996,8 +1140,17 @@ convert_sid (const char *sid)
return (int) tmp_sid;
}
+static GError *
+new_css_no_service_error (void)
+{
+ /* NOTE: update reg_state_css_response() if this error changes */
+ return g_error_new_literal (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_NO_NETWORK,
+ "No service");
+}
+
static void
-serving_system_done (MMSerialPort *port,
+serving_system_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1089,12 +1242,9 @@ serving_system_done (MMSerialPort *port,
sid = 99999;
/* 99999 means unknown/no service */
- if (sid == 99999) {
- /* NOTE: update reg_state_css_response() if this error changes */
- info->error = g_error_new_literal (MM_MOBILE_ERROR,
- MM_MOBILE_ERROR_NO_NETWORK,
- "No service");
- } else {
+ if (sid == 99999)
+ info->error = new_css_no_service_error ();
+ else {
mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (class), NULL);
mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) band), NULL);
mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL);
@@ -1109,39 +1259,97 @@ serving_system_done (MMSerialPort *port,
}
static void
+legacy_get_serving_system (MMGenericCdma *self, MMCallbackInfo *info)
+{
+ MMAtSerialPort *port;
+
+ port = mm_generic_cdma_get_best_at_port (self, &info->error);
+ if (port)
+ mm_at_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info);
+ else
+ mm_callback_info_schedule (info);
+}
+
+static void
+cdma_status_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ QCDMResult *result;
+ guint32 sid, rxstate;
+
+ if (error)
+ goto error;
+
+ /* Parse the response */
+ result = qcdm_cmd_cdma_status_result ((const char *) response->data, response->len, &info->error);
+ if (!result)
+ goto error;
+
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &rxstate);
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &sid);
+ qcdm_result_unref (result);
+
+ if (rxstate == QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA)
+ info->error = new_css_no_service_error ();
+ else {
+ mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (0), NULL);
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) 'Z'), NULL);
+ mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL);
+ }
+
+ mm_callback_info_schedule (info);
+ return;
+
+error:
+ /* If there was some error, fall back to use +CSS like we did before QCDM */
+ legacy_get_serving_system (MM_GENERIC_CDMA (info->modem), info);
+}
+
+static void
get_serving_system (MMModemCdma *modem,
MMModemCdmaServingSystemFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
+ MMGenericCdma *self = MM_GENERIC_CDMA (modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
MMCallbackInfo *info;
- GError *error;
- MMSerialPort *port = priv->primary;
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get serving system while connected");
- callback (modem, 0, 0, 0, error, user_data);
- g_error_free (error);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
- }
info = mm_callback_info_new_full (MM_MODEM (modem),
serving_system_invoke,
G_CALLBACK (callback),
user_data);
- mm_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info);
+ if (priv->qcdm) {
+ GByteArray *cdma_status;
+
+ cdma_status = g_byte_array_sized_new (25);
+ cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25, NULL);
+ g_assert (cdma_status->len);
+ mm_qcdm_serial_port_queue_command (priv->qcdm, cdma_status, 3, cdma_status_cb, info);
+ } else
+ legacy_get_serving_system (self, info);
}
+/*****************************************************************************/
+
+/* Registration state stuff */
+
#define CDMA_1X_STATE_TAG "cdma-1x-reg-state"
#define EVDO_STATE_TAG "evdo-reg-state"
+MMModemCdmaRegistrationState
+mm_generic_cdma_query_reg_state_get_callback_1x_state (MMCallbackInfo *info)
+{
+ g_return_val_if_fail (info != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (info->modem != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_CDMA (info->modem), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+
+ return GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG));
+}
+
void
mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
@@ -1153,6 +1361,16 @@ mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info,
mm_callback_info_set_data (info, CDMA_1X_STATE_TAG, GUINT_TO_POINTER (new_state), NULL);
}
+MMModemCdmaRegistrationState
+mm_generic_cdma_query_reg_state_get_callback_evdo_state (MMCallbackInfo *info)
+{
+ g_return_val_if_fail (info != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (info->modem != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_CDMA (info->modem), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+
+ return GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG));
+}
+
void
mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
@@ -1171,18 +1389,19 @@ registration_state_invoke (MMCallbackInfo *info)
/* note: This is the MMModemCdma interface callback */
callback (MM_MODEM_CDMA (info->modem),
- GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG)),
- GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG)),
+ mm_generic_cdma_query_reg_state_get_callback_1x_state (info),
+ mm_generic_cdma_query_reg_state_get_callback_evdo_state (info),
info->error,
info->user_data);
}
MMCallbackInfo *
mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv;
MMCallbackInfo *info;
g_return_val_if_fail (self != NULL, NULL);
@@ -1195,15 +1414,8 @@ mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self,
user_data);
/* Fill with current state */
- priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- mm_callback_info_set_data (info,
- CDMA_1X_STATE_TAG,
- GUINT_TO_POINTER (priv->cdma_1x_reg_state),
- NULL);
- mm_callback_info_set_data (info,
- EVDO_STATE_TAG,
- GUINT_TO_POINTER (priv->evdo_reg_state),
- NULL);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, cur_cdma_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, cur_evdo_state);
return info;
}
@@ -1211,60 +1423,138 @@ static void
set_callback_1x_state_helper (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
{
- MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+ if (info->modem) {
+ MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
- mm_generic_cdma_set_1x_registration_state (self, new_state);
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state);
+ mm_generic_cdma_set_1x_registration_state (self, new_state);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state);
+ }
}
static void
set_callback_evdo_state_helper (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
{
- MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+ if (info->modem) {
+ MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
- mm_generic_cdma_set_evdo_registration_state (self, new_state);
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state);
+ mm_generic_cdma_set_evdo_registration_state (self, new_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state);
+ }
}
static void
-reg_state_query_done (MMModemCdma *cdma,
- MMModemCdmaRegistrationState cdma_1x_reg_state,
- MMModemCdmaRegistrationState evdo_reg_state,
+subclass_reg_query_done (MMModemCdma *cdma,
+ MMModemCdmaRegistrationState cdma_reg_state,
+ MMModemCdmaRegistrationState evdo_reg_state,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ /* Set final registration state */
+ set_callback_1x_state_helper (info, cdma_reg_state);
+ set_callback_evdo_state_helper (info, evdo_reg_state);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_query_speri_done (MMAtSerialPort *port,
+ GString *response,
GError *error,
gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMCallbackInfo *info = user_data;
+ gboolean roam = FALSE;
+ const char *p;
+
+ if (error)
+ goto done;
+
+ p = mm_strip_tag (response->str, "$SPERI:");
+ if (!p || !mm_cdma_parse_eri (p, &roam, NULL, NULL))
+ goto done;
+
+ /* Change the 1x and EVDO registration states to roaming if they were
+ * anything other than UNKNOWN.
+ */
+ if (roam) {
+ if (mm_generic_cdma_query_reg_state_get_callback_1x_state (info))
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+
+ if (mm_generic_cdma_query_reg_state_get_callback_evdo_state (info))
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_query_spservice_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
if (error)
info->error = g_error_copy (error);
- else {
- set_callback_1x_state_helper (info, cdma_1x_reg_state);
- set_callback_evdo_state_helper (info, evdo_reg_state);
+ else if (mm_cdma_parse_spservice_response (response->str, &cdma_state, &evdo_state)) {
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, cdma_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, evdo_state);
+
+ if (MM_GENERIC_CDMA_GET_PRIVATE (info->modem)->has_speri) {
+ /* Get roaming status to override generic registration state */
+ mm_at_serial_port_queue_command (port, "$SPERI?", 3, reg_query_speri_done, info);
+ return;
+ }
}
mm_callback_info_schedule (info);
}
static void
-query_subclass_registration_state (MMGenericCdma *self, MMCallbackInfo *info)
+real_query_registration_state (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
+ MMModemCdmaRegistrationStateFn callback,
+ gpointer user_data)
{
- /* Let subclasses figure out roaming and detailed registration state */
- if (MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state) {
- MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state (self,
- reg_state_query_done,
- info);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ /* Seed this CallbackInfo with any previously determined registration state */
+ info = mm_generic_cdma_query_reg_state_callback_info_new (self,
+ cur_cdma_state,
+ cur_evdo_state,
+ callback,
+ user_data);
+
+ port = mm_generic_cdma_get_best_at_port (self, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ if (MM_GENERIC_CDMA_GET_PRIVATE (self)->has_spservice) {
+ /* Try Sprint-specific commands */
+ mm_at_serial_port_queue_command (port, "+SPSERVICE?", 3, reg_query_spservice_done, info);
} else {
- /* Or if the subclass doesn't implement more specific checking,
- * assume we're registered.
+ /* Assume we're registered on the 1x network if we passed +CAD, +CSS,
+ * and QCDM Call Manager checking.
*/
- reg_state_query_done (MM_MODEM_CDMA (self),
- MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
- MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
- NULL,
- info);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ mm_callback_info_schedule (info);
}
}
@@ -1282,8 +1572,7 @@ reg_state_css_response (MMModemCdma *cdma,
* report unknown registration state.
*/
if (error) {
- if ( (error->domain == MM_MOBILE_ERROR)
- && (error->code == MM_MOBILE_ERROR_NO_NETWORK)) {
+ if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_NO_NETWORK)) {
set_callback_1x_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
set_callback_evdo_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
} else {
@@ -1291,14 +1580,20 @@ reg_state_css_response (MMModemCdma *cdma,
info->error = g_error_copy (error);
}
mm_callback_info_schedule (info);
- return;
+ } else {
+ /* We're registered on the CDMA 1x network at least, but let subclasses
+ * do more specific registration checking.
+ */
+ MM_GENERIC_CDMA_GET_CLASS (cdma)->query_registration_state (MM_GENERIC_CDMA (info->modem),
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ subclass_reg_query_done,
+ info);
}
-
- query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info);
}
static void
-get_analog_digital_done (MMSerialPort *port,
+get_analog_digital_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1313,7 +1608,7 @@ get_analog_digital_done (MMSerialPort *port,
}
/* Strip any leading command tag and spaces */
- reply = strip_response (response->str, "+CAD:");
+ reply = mm_strip_tag (response->str, "+CAD:");
errno = 0;
int_cad = strtol (reply, NULL, 10);
@@ -1345,7 +1640,11 @@ get_analog_digital_done (MMSerialPort *port,
/* Subclass knows that AT+CSS? will respond incorrectly to EVDO
* state, so skip AT+CSS? query.
*/
- query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info);
+ MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state (MM_GENERIC_CDMA (info->modem),
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ subclass_reg_query_done,
+ info);
}
return;
} else {
@@ -1359,28 +1658,113 @@ error:
}
static void
+reg_cmstate_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMAtSerialPort *at_port;
+ QCDMResult *result;
+ guint32 opmode = 0, sysmode = 0;
+ MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+
+ if (error)
+ goto error;
+
+ /* Parse the response */
+ result = qcdm_cmd_cm_subsys_state_info_result ((const char *) response->data, response->len, &info->error);
+ if (!result)
+ goto error;
+
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &opmode);
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &sysmode);
+ qcdm_result_unref (result);
+
+ if (opmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) {
+ switch (sysmode) {
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA:
+ cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR:
+ evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS:
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE:
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA:
+ default:
+ break;
+ }
+
+ if (cdma_state || evdo_state) {
+ /* Device is registered to something; see if the subclass has a
+ * better idea of whether we're roaming or not and what the
+ * access technology is.
+ */
+ if (MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state) {
+ MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state (MM_GENERIC_CDMA (info->modem),
+ cdma_state,
+ evdo_state,
+ subclass_reg_query_done,
+ info);
+ return;
+ }
+ }
+ }
+
+ set_callback_1x_state_helper (info, cdma_state);
+ set_callback_evdo_state_helper (info, evdo_state);
+ mm_callback_info_schedule (info);
+ return;
+
+error:
+ /* If there was some error, fall back to use +CAD like we did before QCDM */
+ at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (info->modem), &info->error);
+ if (at_port)
+ mm_at_serial_port_queue_command (at_port, "+CAD?", 3, get_analog_digital_done, info);
+ else
+ mm_callback_info_schedule (info);
+}
+
+static void
get_registration_state (MMModemCdma *modem,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- g_message ("Returning saved registration states: 1x: %d EVDO: %d",
- priv->cdma_1x_reg_state, priv->evdo_reg_state);
- callback (MM_MODEM_CDMA (modem), priv->cdma_1x_reg_state, priv->evdo_reg_state, NULL, user_data);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
+ MMAtSerialPort *port;
+
+ info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem),
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ callback,
+ user_data);
+
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error);
+ if (!port && !priv->qcdm) {
+ g_message ("Returning saved registration states: 1x: %d EVDO: %d",
+ priv->cdma_1x_reg_state, priv->evdo_reg_state);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state);
+ mm_callback_info_schedule (info);
+ return;
}
- info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem), callback, user_data);
- mm_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info);
+ /* Use QCDM for Call Manager state or HDR state before trying CAD, since
+ * CAD doesn't always reflect the state of the HDR radio's registration
+ * status.
+ */
+ if (priv->qcdm) {
+ GByteArray *cmstate;
+
+ cmstate = g_byte_array_sized_new (25);
+ cmstate->len = qcdm_cmd_cm_subsys_state_info_new ((char *) cmstate->data, 25, NULL);
+ g_assert (cmstate->len);
+ mm_qcdm_serial_port_queue_command (priv->qcdm, cmstate, 3, reg_cmstate_cb, info);
+ } else
+ mm_at_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info);
}
/*****************************************************************************/
@@ -1512,10 +1896,9 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
const char *str;
guint id;
- if (error) {
- info->error = g_error_copy (error);
+ info->error = mm_modem_check_removed (modem, error);
+ if (info->error)
goto out;
- }
switch (state) {
case SIMPLE_STATE_BEGIN:
@@ -1550,7 +1933,8 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
out:
if (info->error || state == SIMPLE_STATE_DONE) {
- registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0);
+ if (modem)
+ registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0);
mm_callback_info_schedule (info);
}
}
@@ -1609,16 +1993,26 @@ simple_uint_value (guint32 i)
return val;
}
+#define SS_HASH_TAG "simple-get-status"
+
static void
simple_status_got_signal_quality (MMModem *modem,
guint32 result,
GError *error,
gpointer user_data)
{
- if (error)
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ if (error) {
+ info->error = g_error_copy (error);
g_warning ("Error getting signal quality: %s", error->message);
- else
- g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result));
+ } else {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "signal_quality", simple_uint_value (result));
+ }
+
+ mm_callback_info_schedule (info);
}
static void
@@ -1627,7 +2021,7 @@ simple_get_status_invoke (MMCallbackInfo *info)
MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback;
callback (MM_MODEM_SIMPLE (info->modem),
- (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"),
+ (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG),
info->error, info->user_data);
}
@@ -1644,9 +2038,9 @@ simple_get_status (MMModemSimple *simple,
G_CALLBACK (callback),
user_data);
- properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue);
- mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref);
- mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, properties);
+ properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue);
+ mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref);
+ mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, info);
}
/*****************************************************************************/
@@ -1659,6 +2053,28 @@ modem_valid_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data)
registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL);
}
+static void
+modem_state_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data)
+{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+ MMModemState state;
+
+ /* Start polling registration status and signal quality when enabled */
+
+ state = mm_modem_get_state (MM_MODEM (self));
+ if (state >= MM_MODEM_STATE_ENABLED) {
+ if (!priv->poll_id) {
+ priv->poll_id = g_timeout_add_seconds (30, periodic_poll_cb, self);
+ /* Kick one off immediately */
+ periodic_poll_cb (self);
+ }
+ } else {
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+}
+
/*****************************************************************************/
static void
@@ -1690,27 +2106,13 @@ modem_simple_init (MMModemSimple *class)
class->get_status = simple_get_status;
}
-static GObject*
-constructor (GType type,
- guint n_construct_params,
- GObjectConstructParam *construct_params)
-{
- GObject *object;
-
- object = G_OBJECT_CLASS (mm_generic_cdma_parent_class)->constructor (type,
- n_construct_params,
- construct_params);
- if (object) {
- g_signal_connect (object, "notify::" MM_MODEM_VALID,
- G_CALLBACK (modem_valid_changed), NULL);
- }
-
- return object;
-}
-
static void
mm_generic_cdma_init (MMGenericCdma *self)
{
+ g_signal_connect (self, "notify::" MM_MODEM_VALID,
+ G_CALLBACK (modem_valid_changed), NULL);
+ g_signal_connect (self, "notify::" MM_MODEM_STATE,
+ G_CALLBACK (modem_state_changed), NULL);
}
static void
@@ -1771,15 +2173,15 @@ get_property (GObject *object, guint prop_id,
static void
dispose (GObject *object)
{
- registration_cleanup (MM_GENERIC_CDMA (object), MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL);
+ MMGenericCdma *self = MM_GENERIC_CDMA (object);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object);
-}
+ registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL);
-static void
-finalize (GObject *object)
-{
- G_OBJECT_CLASS (mm_generic_cdma_parent_class)->finalize (object);
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+
+ G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object);
}
static void
@@ -1794,8 +2196,7 @@ mm_generic_cdma_class_init (MMGenericCdmaClass *klass)
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->dispose = dispose;
- object_class->finalize = finalize;
- object_class->constructor = constructor;
+ klass->query_registration_state = real_query_registration_state;
/* Properties */
g_object_class_override_property (object_class,
diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h
index 5b4a0b6..e4f9e57 100644
--- a/src/mm-generic-cdma.h
+++ b/src/mm-generic-cdma.h
@@ -20,7 +20,7 @@
#include "mm-modem.h"
#include "mm-modem-base.h"
#include "mm-modem-cdma.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
#include "mm-callback-info.h"
#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ())
@@ -42,7 +42,27 @@ typedef struct {
typedef struct {
MMModemBaseClass parent;
+ /* Subclasses should implement this function if they can more accurately
+ * determine the registration state and/or roaming status than the base
+ * class can (by using manufacturer custom AT commands or whatever).
+ * The base class passes its detected registration state in the
+ * cur_cdma_state and cur_evdo_state arguments, which the subclass should
+ * override if necessary before passing to the callback.
+ *
+ * Subclasses can use the helper functions
+ * mm_generic_cdma_query_reg_state_callback_info_new(),
+ * mm_generic_cdma_query_reg_state_set_callback_1x_state(), and
+ * mm_generic_cdma_query_reg_state_set_callback_evdo_state() to create the
+ * MMCallbackInfo object and to set the registration state which is passed
+ * to the callback when the subclass' registration query completes.
+ *
+ * Subclasses should generally not return parsing or other non-critical
+ * errors to the callback since that fails the entire registration check,
+ * rendering the superclass' checks useless.
+ */
void (*query_registration_state) (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data);
@@ -71,8 +91,6 @@ MMModem *mm_generic_cdma_new (const char *device,
/* Private, for subclasses */
-#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state"
-
MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self,
const char *subsys,
const char *name,
@@ -80,7 +98,10 @@ MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self,
gpointer user_data,
GError **error);
-MMSerialPort *mm_generic_cdma_get_port (MMGenericCdma *modem, MMPortType ptype);
+MMAtSerialPort *mm_generic_cdma_get_at_port (MMGenericCdma *modem, MMPortType ptype);
+
+MMAtSerialPort *mm_generic_cdma_get_best_at_port (MMGenericCdma *modem,
+ GError **error);
void mm_generic_cdma_update_cdma1x_quality (MMGenericCdma *self, guint32 quality);
void mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality);
@@ -99,12 +120,18 @@ MMModemCdmaRegistrationState mm_generic_cdma_evdo_get_registration_state_sync (M
/* query_registration_state class function helpers */
MMCallbackInfo *mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data);
+MMModemCdmaRegistrationState mm_generic_cdma_query_reg_state_get_callback_1x_state (MMCallbackInfo *info);
+
void mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state);
+MMModemCdmaRegistrationState mm_generic_cdma_query_reg_state_get_callback_evdo_state (MMCallbackInfo *info);
+
void mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state);
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 4954ca1..08cde10 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -12,13 +12,15 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
- * Copyright (C) 2009 Ericsson
+ * Copyright (C) 2009 - 2010 Ericsson
*/
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+
#include "mm-generic-gsm.h"
#include "mm-modem-gsm-card.h"
#include "mm-modem-gsm-network.h"
@@ -26,8 +28,13 @@
#include "mm-modem-simple.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-modem-helpers.h"
+#include "mm-options.h"
+#include "mm-properties-changed-signal.h"
+#include "mm-utils.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -50,39 +57,88 @@ typedef struct {
char *device;
gboolean valid;
+ gboolean pin_checked;
+ guint32 pin_check_tries;
+ guint pin_check_timeout;
+
+ MMModemGsmAllowedMode allowed_mode;
+
+ gboolean roam_allowed;
char *oper_code;
char *oper_name;
guint32 ip_method;
- gboolean unsolicited_registration;
- MMModemGsmNetworkRegStatus reg_status;
+ GPtrArray *reg_regex;
+
+ guint poll_id;
+
+ /* CREG and CGREG info */
+ gboolean creg_poll;
+ gboolean cgreg_poll;
+ /* Index 0 for CREG, index 1 for CGREG */
+ gulong lac[2];
+ gulong cell_id[2];
+ MMModemGsmAccessTech act;
+
+ /* Index 0 for CREG, index 1 for CGREG */
+ MMModemGsmNetworkRegStatus reg_status[2];
guint pending_reg_id;
MMCallbackInfo *pending_reg_info;
+ gboolean manual_reg;
+ guint signal_quality_id;
+ time_t signal_quality_timestamp;
guint32 signal_quality;
- guint32 cid;
+ gint cid;
+
+ guint32 charsets;
+ guint32 cur_charset;
- MMSerialPort *primary;
- MMSerialPort *secondary;
+ MMAtSerialPort *primary;
+ MMAtSerialPort *secondary;
+ MMQcdmSerialPort *qcdm;
MMPort *data;
} MMGenericGsmPrivate;
-static void get_registration_status (MMSerialPort *port, MMCallbackInfo *info);
-static void read_operator_code_done (MMSerialPort *port,
+static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info);
+static void read_operator_code_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data);
-static void read_operator_name_done (MMSerialPort *port,
+static void read_operator_name_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data);
-static void reg_state_changed (MMSerialPort *port,
+static void reg_state_changed (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data);
+static void get_reg_status_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static gboolean handle_reg_status_response (MMGenericGsm *self,
+ GString *response,
+ GError **error);
+
+static MMModemGsmAccessTech etsi_act_to_mm_act (gint act);
+
+static void _internal_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act);
+
+static void reg_info_updated (MMGenericGsm *self,
+ gboolean update_rs,
+ MMGenericGsmRegType rs_type,
+ MMModemGsmNetworkRegStatus status,
+ gboolean update_code,
+ const char *oper_code,
+ gboolean update_name,
+ const char *oper_name);
+
MMModem *
mm_generic_gsm_new (const char *device,
const char *driver,
@@ -99,24 +155,7 @@ mm_generic_gsm_new (const char *device,
NULL));
}
-void
-mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
- gboolean enabled)
-{
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- MM_GENERIC_GSM_GET_PRIVATE (modem)->unsolicited_registration = enabled;
-}
-
-void
-mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid)
-{
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- MM_GENERIC_GSM_GET_PRIVATE (modem)->cid = cid;
-}
-
-guint32
+gint
mm_generic_gsm_get_cid (MMGenericGsm *modem)
{
g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), 0);
@@ -124,93 +163,98 @@ 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)
-{
- MMGenericGsmPrivate *priv;
-
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- priv = MM_GENERIC_GSM_GET_PRIVATE (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_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem);
- mm_serial_port_queue_command (priv->primary, "+COPS=3,0;+COPS?", 3, read_operator_name_done, modem);
- 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);
- priv->oper_code = priv->oper_name = NULL;
+typedef struct {
+ const char *result;
+ const char *normalized;
+ guint code;
+} CPinResult;
- mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (modem), priv->reg_status,
- priv->oper_code, priv->oper_name);
- }
+static CPinResult unlock_results[] = {
+ /* Longer entries first so we catch the correct one with strcmp() */
+ { "PH-NETSUB PIN", "ph-netsub-pin", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
+ { "PH-NETSUB PUK", "ph-netsub-puk", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
+ { "PH-FSIM PIN", "ph-fsim-pin", MM_MOBILE_ERROR_PH_FSIM_PIN },
+ { "PH-FSIM PUK", "ph-fsim-puk", MM_MOBILE_ERROR_PH_FSIM_PUK },
+ { "PH-CORP PIN", "ph-corp-pin", MM_MOBILE_ERROR_CORP_PIN },
+ { "PH-CORP PUK", "ph-corp-puk", MM_MOBILE_ERROR_CORP_PUK },
+ { "PH-SIM PIN", "ph-sim-pin", MM_MOBILE_ERROR_PH_SIM_PIN },
+ { "PH-NET PIN", "ph-net-pin", MM_MOBILE_ERROR_NETWORK_PIN },
+ { "PH-NET PUK", "ph-net-puk", MM_MOBILE_ERROR_NETWORK_PUK },
+ { "PH-SP PIN", "ph-sp-pin", MM_MOBILE_ERROR_SERVICE_PIN },
+ { "PH-SP PUK", "ph-sp-puk", MM_MOBILE_ERROR_SERVICE_PUK },
+ { "SIM PIN2", "sim-pin2", MM_MOBILE_ERROR_SIM_PIN2 },
+ { "SIM PUK2", "sim-puk2", MM_MOBILE_ERROR_SIM_PUK2 },
+ { "SIM PIN", "sim-pin", MM_MOBILE_ERROR_SIM_PIN },
+ { "SIM PUK", "sim-puk", MM_MOBILE_ERROR_SIM_PUK },
+ { NULL, NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
+};
+
+static GError *
+error_for_unlock_required (const char *unlock)
+{
+ CPinResult *iter = &unlock_results[0];
+
+ if (!unlock || !strlen (unlock))
+ return NULL;
- mm_generic_gsm_update_enabled_state (modem, TRUE, MM_MODEM_STATE_REASON_NONE);
+ /* Translate the error */
+ while (iter->result) {
+ if (!strcmp (iter->normalized, unlock))
+ return mm_mobile_error_for_code (iter->code);
+ iter++;
}
+
+ return g_error_new (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_UNKNOWN,
+ "Unknown unlock request '%s'", unlock);
}
-typedef struct {
- const char *result;
- guint code;
-} CPinResult;
+static void
+get_unlock_retries_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error)
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), result);
+ else
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED);
+}
static void
-pin_check_done (MMSerialPort *port,
+pin_check_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean parsed = FALSE;
- static CPinResult results[] = {
- { "SIM PIN", MM_MOBILE_ERROR_SIM_PIN },
- { "SIM PUK", MM_MOBILE_ERROR_SIM_PUK },
- { "PH-SIM PIN", MM_MOBILE_ERROR_PH_SIM_PIN },
- { "PH-FSIM PIN", MM_MOBILE_ERROR_PH_FSIM_PIN },
- { "PH-FSIM PUK", MM_MOBILE_ERROR_PH_FSIM_PUK },
- { "SIM PIN2", MM_MOBILE_ERROR_SIM_PIN2 },
- { "SIM PUK2", MM_MOBILE_ERROR_SIM_PUK2 },
- { "PH-NET PIN", MM_MOBILE_ERROR_NETWORK_PIN },
- { "PH-NET PUK", MM_MOBILE_ERROR_NETWORK_PUK },
- { "PH-NETSUB PIN", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
- { "PH-NETSUB PUK", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
- { "PH-SP PIN", MM_MOBILE_ERROR_SERVICE_PIN },
- { "PH-SP PUK", MM_MOBILE_ERROR_SERVICE_PUK },
- { "PH-CORP PIN", MM_MOBILE_ERROR_CORP_PIN },
- { "PH-CORP PUK", MM_MOBILE_ERROR_CORP_PUK },
- { NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
- };
if (error)
info->error = g_error_copy (error);
- else if (g_str_has_prefix (response->str, "+CPIN: ")) {
- const char *str = response->str + 7;
+ else if (response && strstr (response->str, "+CPIN: ")) {
+ const char *str = strstr (response->str, "+CPIN: ") + 7;
- if (g_str_has_prefix (str, "READY"))
+ if (g_str_has_prefix (str, "READY")) {
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+ if (MM_MODEM_GSM_CARD_GET_INTERFACE (info->modem)->get_unlock_retries)
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
+ else
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem),
+ MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED);
parsed = TRUE;
- else {
- CPinResult *iter = &results[0];
+ } else {
+ CPinResult *iter = &unlock_results[0];
/* Translate the error */
while (iter->result) {
if (g_str_has_prefix (str, iter->result)) {
info->error = mm_mobile_error_for_code (iter->code);
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), iter->normalized);
+ mm_modem_gsm_card_get_unlock_retries (MM_MODEM_GSM_CARD (info->modem),
+ iter->normalized,
+ get_unlock_retries_cb,
+ NULL);
parsed = TRUE;
break;
}
@@ -219,20 +263,26 @@ pin_check_done (MMSerialPort *port,
}
}
- if (!info->error && !parsed) {
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse PIN request response '%s'",
- response->str);
+ if (!parsed) {
+ /* Assume unlocked if we don't recognize the pin request result */
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
+
+ if (!info->error) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse PIN request response '%s'",
+ response->str);
+ }
}
mm_callback_info_schedule (info);
}
-void
-mm_generic_gsm_check_pin (MMGenericGsm *modem,
- MMModemFn callback,
- gpointer user_data)
+static void
+check_pin (MMGenericGsm *modem,
+ MMModemFn callback,
+ gpointer user_data)
{
MMGenericGsmPrivate *priv;
MMCallbackInfo *info;
@@ -241,25 +291,66 @@ mm_generic_gsm_check_pin (MMGenericGsm *modem,
priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
+ mm_at_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
+}
+
+static void
+get_imei_cb (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_modem_base_set_equipment_identifier (MM_MODEM_BASE (modem), error ? "" : result);
+ mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_GSM_GET_PRIVATE (modem)->primary));
+ }
}
/*****************************************************************************/
+static MMModemGsmNetworkRegStatus
+gsm_reg_status (MMGenericGsm *self)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ /* Some devices (Blackberries for example) will respond to +CGREG, but
+ * return ERROR for +CREG, probably because their firmware is just stupid.
+ * So here we prefer the +CREG response, but if we never got a successful
+ * +CREG response, we'll take +CGREG instead.
+ */
+
+ if ( priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)
+ return priv->reg_status[0];
+
+ if ( priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)
+ return priv->reg_status[1];
+
+ if (priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING)
+ return priv->reg_status[0];
+
+ if (priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING)
+ return priv->reg_status[1];
+
+ if (priv->reg_status[0] != MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN)
+ return priv->reg_status[0];
+
+ return priv->reg_status[1];
+}
+
void
mm_generic_gsm_update_enabled_state (MMGenericGsm *self,
gboolean stay_connected,
MMModemStateReason reason)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
-
/* While connected we don't want registration status changes to change
* the modem's state away from CONNECTED.
*/
if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING))
return;
- switch (priv->reg_status) {
+ switch (gsm_reg_status (self)) {
case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_REGISTERED, reason);
@@ -282,12 +373,109 @@ check_valid (MMGenericGsm *self)
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
gboolean new_valid = FALSE;
- if (priv->primary && priv->data)
+ if (priv->primary && priv->data && priv->pin_checked)
new_valid = TRUE;
mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid);
}
+
+static void initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data);
+
+static gboolean
+pin_check_again (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->pin_check_timeout = 0;
+ check_pin (self, initial_pin_check_done, NULL);
+ return FALSE;
+}
+
+static void
+initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMGenericGsmPrivate *priv;
+
+ /* modem could have been removed before we get here, in which case
+ * 'modem' will be NULL.
+ */
+ if (!modem)
+ return;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if ( error
+ && priv->pin_check_tries++ < 3
+ && !mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem))) {
+ /* Try it again a few times */
+ if (priv->pin_check_timeout)
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = g_timeout_add_seconds (2, pin_check_again, modem);
+ } else {
+ priv->pin_checked = TRUE;
+ mm_serial_port_close (MM_SERIAL_PORT (priv->primary));
+ check_valid (MM_GENERIC_GSM (modem));
+ }
+}
+
+static void
+initial_pin_check (MMGenericGsm *self)
+{
+ GError *error = NULL;
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error))
+ check_pin (self, initial_pin_check_done, NULL);
+ else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+
+ /* Ensure the modem is still somewhat usable if opening the serial
+ * port fails for some reason.
+ */
+ initial_pin_check_done (MM_MODEM (self), NULL, NULL);
+ }
+}
+
+static void
+initial_imei_check (MMGenericGsm *self)
+{
+ GError *error = NULL;
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ /* Make sure echoing is off */
+ mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL);
+
+ /* Get modem's imei number */
+ mm_modem_gsm_card_get_imei (MM_MODEM_GSM_CARD (self),
+ get_imei_cb,
+ NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+}
+
static gboolean
owns_port (MMModem *modem, const char *subsys, const char *name)
{
@@ -308,28 +496,50 @@ mm_generic_gsm_grab_port (MMGenericGsm *self,
g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE);
port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
- if (port && MM_IS_SERIAL_PORT (port)) {
- mm_serial_port_set_response_parser (MM_SERIAL_PORT (port),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ if (!port) {
+ g_warn_if_fail (port != NULL);
+ return NULL;
+ }
+
+ if (MM_IS_AT_SERIAL_PORT (port)) {
+ GPtrArray *array;
+ int i;
+
+ mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
+ 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_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
- g_regex_unref (regex);
+ /* Set up CREG unsolicited message handlers */
+ array = mm_gsm_creg_regex_get (FALSE);
+ for (i = 0; i < array->len; i++) {
+ regex = g_ptr_array_index (array, i);
+
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
+ }
+ mm_gsm_creg_regex_destroy (array);
if (ptype == MM_PORT_TYPE_PRIMARY) {
- priv->primary = MM_SERIAL_PORT (port);
+ priv->primary = MM_AT_SERIAL_PORT (port);
if (!priv->data) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
}
- check_valid (self);
+
+ /* Get modem's initial lock/unlock state */
+ initial_pin_check (self);
+
+ /* Get modem's IMEI number */
+ initial_imei_check (self);
+
} else if (ptype == MM_PORT_TYPE_SECONDARY)
- priv->secondary = MM_SERIAL_PORT (port);
- } else {
+ priv->secondary = MM_AT_SERIAL_PORT (port);
+ } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
+ if (!priv->qcdm)
+ priv->qcdm = MM_QCDM_SERIAL_PORT (port);
+ } else if (!strcmp (subsys, "net")) {
/* Net device (if any) is the preferred data port */
- if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) {
+ if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
check_valid (self);
@@ -381,7 +591,7 @@ release_port (MMModem *modem, const char *subsys, const char *name)
if (!port)
return;
- if (port == MM_PORT (priv->primary)) {
+ if (port == (MMPort *) priv->primary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->primary = NULL;
}
@@ -391,59 +601,375 @@ release_port (MMModem *modem, const char *subsys, const char *name)
g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE);
}
- if (port == MM_PORT (priv->secondary)) {
+ if (port == (MMPort *) priv->secondary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->secondary = NULL;
}
+ if (port == (MMPort *) priv->qcdm) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->qcdm = NULL;
+ }
+
check_valid (MM_GENERIC_GSM (modem));
}
+static void
+reg_poll_response (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+
+ if (!error)
+ handle_reg_status_response (self, response, NULL);
+}
+
+static void
+periodic_signal_quality_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ /* Cached signal quality already updated */
+}
+
+static void
+periodic_access_tech_cb (MMModem *modem,
+ guint32 act,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem && !error && act)
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (modem), act);
+}
+
+static gboolean
+periodic_poll_cb (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMAtSerialPort *port;
+
+ port = mm_generic_gsm_get_best_at_port (self, NULL);
+ if (!port)
+ return TRUE; /* oh well, try later */
+
+ if (priv->creg_poll)
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, self);
+ if (priv->cgreg_poll)
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, self);
+
+ mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (self),
+ periodic_signal_quality_cb,
+ NULL);
+
+ if (MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology)
+ MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology (self, periodic_access_tech_cb, NULL);
+
+ return TRUE; /* continue running */
+}
+
+static void
+cgreg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ g_clear_error (&info->error);
+
+ /* The modem doesn't like unsolicited CGREG, so we'll need to poll */
+ priv->cgreg_poll = TRUE;
+ }
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+ }
+ mm_callback_info_schedule (info);
+}
+
+static void
+cgreg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Ignore errors except modem removal errors */
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ g_clear_error (&info->error);
+ /* Try CGREG=1 instead */
+ mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info);
+ } else {
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+
+ /* All done */
+ mm_callback_info_schedule (info);
+ }
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+creg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ if (info->error) {
+ g_clear_error (&info->error);
+
+ /* The modem doesn't like unsolicited CREG, so we'll need to poll */
+ priv->creg_poll = TRUE;
+ }
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+creg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Ignore errors except modem removal errors */
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ g_clear_error (&info->error);
+ mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info);
+ } else {
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ }
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+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_force (MM_SERIAL_PORT (priv->primary));
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close_force (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)
+{
+ if (modem) {
+ mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (modem),
+ error ? MM_MODEM_GSM_ALLOWED_MODE_ANY : mode);
+ }
+}
+
+static void
+get_enable_info_done (MMModem *modem,
+ const char *manufacturer,
+ const char *model,
+ const char *version,
+ GError *error,
+ gpointer user_data)
+{
+ /* Modem base class handles the response for us */
+}
+
void
-mm_generic_gsm_enable_complete (MMGenericGsm *modem,
+mm_generic_gsm_enable_complete (MMGenericGsm *self,
GError *error,
MMCallbackInfo *info)
{
- g_return_if_fail (modem != NULL);
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
g_return_if_fail (info != NULL);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
if (error) {
- mm_modem_set_state (MM_MODEM (modem),
- MM_MODEM_STATE_DISABLED,
- MM_MODEM_STATE_REASON_NONE);
+ enable_failed ((MMModem *) self, error, info);
+ return;
+ }
- info->error = g_error_copy (error);
- } else {
- /* Modem is enabled; update the state */
- mm_generic_gsm_update_enabled_state (modem, FALSE, MM_MODEM_STATE_REASON_NONE);
+ /* Open the second port here if the modem has one. We'll use it for
+ * signal strength and registration updates when the device is connected,
+ * but also many devices will send unsolicited registration or other
+ * messages to the secondary port but not the primary.
+ */
+ if (priv->secondary) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &error)) {
+ if (mm_options_debug ()) {
+ g_warning ("%s: error opening secondary port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ }
+ }
}
- mm_callback_info_schedule (info);
+ /* Try to enable XON/XOFF flow control */
+ mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL);
+
+ /* Grab device info right away */
+ mm_modem_get_info (MM_MODEM (self), get_enable_info_done, NULL);
+
+ /* Get allowed mode */
+ 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);
+
+ /* And supported character sets */
+ mm_modem_get_supported_charsets (MM_MODEM (self), supported_charsets_done, info);
}
static void
-enable_done (MMSerialPort *port,
+real_do_enable_power_up_done (MMGenericGsm *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ /* Ignore power-up errors as not all devices actually support CFUN=1 */
+ mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
+}
+
+static void
+enable_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- /* Ignore power-up command errors, not all devices actually support
- * CFUN=1.
- */
- /* FIXME: instead of just ignoring errors, since we allow subclasses
- * to override the power-on command, make a class function for powering
- * on the phone and let the subclass decided whether it wants to handle
- * errors or ignore them.
+ /* Let subclasses handle the power up command response/error; many devices
+ * don't support +CFUN, but for those that do let them handle the error
+ * correctly.
*/
-
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
+ g_assert (MM_GENERIC_GSM_GET_CLASS (info->modem)->do_enable_power_up_done);
+ MM_GENERIC_GSM_GET_CLASS (info->modem)->do_enable_power_up_done (MM_GENERIC_GSM (info->modem),
+ response,
+ error,
+ info);
}
static void
-init_done (MMSerialPort *port,
+init_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -457,22 +983,22 @@ init_done (MMSerialPort *port,
}
/* Ensure echo is off after the init command; some modems ignore the
- * E0 when it's in the same like as ATZ (Option GIO322).
- */
- mm_serial_port_queue_command (port, "E0 +CMEE=1", 2, NULL, NULL);
+ * E0 when it's in the same line as ATZ (Option GIO322).
+ */
+ mm_at_serial_port_queue_command (port, "E0", 2, NULL, NULL);
+
+ /* Some phones (like Blackberries) don't support +CMEE=1, so make it
+ * optional. It completely violates 3GPP TS 27.007 (9.1) but what can we do...
+ */
+ mm_at_serial_port_queue_command (port, "+CMEE=1", 2, NULL, NULL);
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD_OPTIONAL, &cmd, NULL);
- mm_serial_port_queue_command (port, cmd, 2, NULL, NULL);
+ mm_at_serial_port_queue_command (port, cmd, 2, NULL, NULL);
g_free (cmd);
- if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->unsolicited_registration)
- mm_serial_port_queue_command (port, "+CREG=1", 5, NULL, NULL);
- else
- mm_serial_port_queue_command (port, "+CREG=0", 5, NULL, NULL);
-
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_UP_CMD, &cmd, NULL);
if (cmd && strlen (cmd))
- mm_serial_port_queue_command (port, cmd, 5, enable_done, user_data);
+ mm_at_serial_port_queue_command (port, cmd, 5, enable_done, user_data);
else
enable_done (port, NULL, NULL, user_data);
g_free (cmd);
@@ -490,7 +1016,7 @@ enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
}
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD, &cmd, NULL);
- mm_serial_port_queue_command (port, cmd, 3, init_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 3, init_done, user_data);
g_free (cmd);
}
@@ -501,7 +1027,7 @@ real_do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data)
MMCallbackInfo *info;
info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
- mm_serial_port_flash (priv->primary, 100, enable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 100, FALSE, enable_flash_done, info);
}
static void
@@ -511,11 +1037,25 @@ enable (MMModem *modem,
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
GError *error = NULL;
+ const char *unlock;
+
+ /* If the device needs a PIN, deal with that now, but we don't care
+ * about SIM-PIN2/SIM-PUK2 since the device is operational without it.
+ */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock && strcmp (unlock, "sim-puk2") && strcmp (unlock, "sim-pin2")) {
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+ info->error = error_for_unlock_required (unlock);
+ mm_callback_info_schedule (info);
+ return;
+ }
/* First, reset the previously used CID */
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
+ priv->cid = -1;
- if (!mm_serial_port_open (priv->primary, &error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
MMCallbackInfo *info;
g_assert (error);
@@ -532,7 +1072,7 @@ enable (MMModem *modem,
}
static void
-disable_done (MMSerialPort *port,
+disable_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -541,13 +1081,25 @@ disable_done (MMSerialPort *port,
info->error = mm_modem_check_removed (info->modem, error);
if (!info->error) {
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
- mm_serial_port_close (port);
+ mm_serial_port_close_force (MM_SERIAL_PORT (port));
mm_modem_set_state (MM_MODEM (info->modem),
MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_REASON_NONE);
- priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
+
+ /* Clear out circuit-switched registration info... */
+ reg_info_updated (self,
+ MM_GENERIC_GSM_REG_TYPE_CS,
+ TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN,
+ TRUE, NULL,
+ TRUE, NULL);
+ /* ...and packet-switched registration info */
+ reg_info_updated (self,
+ MM_GENERIC_GSM_REG_TYPE_PS,
+ TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN,
+ TRUE, NULL,
+ TRUE, NULL);
}
mm_callback_info_schedule (info);
}
@@ -575,11 +1127,15 @@ disable_flash_done (MMSerialPort *port,
return;
}
+ /* Disable unsolicited messages */
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT+CREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT+CGREG=0", 3, NULL, NULL);
+
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_DOWN_CMD, &cmd, NULL);
if (cmd && strlen (cmd))
- mm_serial_port_queue_command (port, cmd, 5, disable_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 5, disable_done, user_data);
else
- disable_done (port, NULL, NULL, user_data);
+ disable_done (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data);
g_free (cmd);
}
@@ -588,14 +1144,42 @@ disable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
MMModemState state;
/* First, reset the previously used CID and clean up registration */
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
+ g_warn_if_fail (priv->cid == -1);
+ priv->cid = -1;
+
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ if (priv->signal_quality_id) {
+ g_source_remove (priv->signal_quality_id);
+ priv->signal_quality_id = 0;
+ }
+
+ if (priv->pin_check_timeout) {
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
+
+ priv->lac[0] = 0;
+ priv->lac[1] = 0;
+ priv->cell_id[0] = 0;
+ priv->cell_id[1] = 0;
+ _internal_update_access_technology (self, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+
+ /* Close the secondary port if its open */
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary));
+
info = mm_callback_info_new (modem, callback, user_data);
/* Cache the previous state so we can reset it if the operation fails */
@@ -610,13 +1194,13 @@ disable (MMModem *modem,
MM_MODEM_STATE_REASON_NONE);
if (mm_port_get_connected (MM_PORT (priv->primary)))
- mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disable_flash_done, info);
else
- disable_flash_done (priv->primary, NULL, info);
+ disable_flash_done (MM_SERIAL_PORT (priv->primary), NULL, info);
}
static void
-get_string_done (MMSerialPort *port,
+get_string_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -632,6 +1216,110 @@ get_string_done (MMSerialPort *port,
}
static void
+get_mnc_length_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int sw1, sw2;
+ const char *imsi;
+ gboolean success = FALSE;
+ char hex[51];
+ char *bin;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response->str, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response->str, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the CRSM response");
+ goto done;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) {
+ gsize buflen = 0;
+ guint32 mnc_len;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = utils_hexstr2bin (hex, &buflen);
+ if (!bin || buflen < 4) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned malformed response '%s'",
+ hex);
+ goto done;
+ }
+
+ /* MNC length is byte 4 of this SIM file */
+ mnc_len = bin[3] & 0xFF;
+ if (mnc_len == 2 || mnc_len == 3) {
+ imsi = mm_callback_info_get_data (info, "imsi");
+ mm_callback_info_set_result (info, g_strndup (imsi, 3 + mnc_len), g_free);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)",
+ mnc_len);
+ }
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_operator_id_imsi_done (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_callback_info_set_data (info, "imsi", g_strdup (result), g_free);
+
+ /* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
+ mm_at_serial_port_queue_command_cached (priv->primary,
+ "+CRSM=176,28589,0,0,4",
+ 3,
+ get_mnc_length_done,
+ info);
+}
+
+static void
get_imei (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
@@ -640,7 +1328,7 @@ get_imei (MMModemGsmCard *modem,
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
+ mm_at_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
}
static void
@@ -652,112 +1340,116 @@ get_imsi (MMModemGsmCard *modem,
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
+ mm_at_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
}
static void
-card_info_invoke (MMCallbackInfo *info)
+get_operator_id (MMModemGsmCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
{
- MMModemInfoFn callback = (MMModemInfoFn) info->callback;
-
- callback (info->modem,
- (char *) mm_callback_info_get_data (info, "card-info-manufacturer"),
- (char *) mm_callback_info_get_data (info, "card-info-model"),
- (char *) mm_callback_info_get_data (info, "card-info-version"),
- info->error, info->user_data);
-}
-
-#define GMI_RESP_TAG "+CGMI:"
-#define GMM_RESP_TAG "+CGMM:"
-#define GMR_RESP_TAG "+CGMR:"
+ MMCallbackInfo *info;
-static const char *
-strip_tag (const char *str, const char *tag)
-{
- /* Strip the response header, if any */
- if (strncmp (str, tag, strlen (tag)) == 0)
- str += strlen (tag);
- while (*str && isspace (*str))
- str++;
- return str;
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+ mm_modem_gsm_card_get_imsi (MM_MODEM_GSM_CARD (modem),
+ get_operator_id_imsi_done,
+ info);
}
static void
-get_version_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+get_card_info (MMModem *modem,
+ MMModemInfoFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *resp = strip_tag (response->str, GMR_RESP_TAG);
-
- if (!error)
- mm_callback_info_set_data (info, "card-info-version", g_strdup (resp), g_free);
- else if (!info->error)
- info->error = g_error_copy (error);
+ MMAtSerialPort *port;
+ GError *error = NULL;
- mm_callback_info_schedule (info);
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &error);
+ mm_modem_base_get_card_info (MM_MODEM_BASE (modem), port, error, callback, user_data);
+ g_clear_error (&error);
}
+#define PIN_PORT_TAG "pin-port"
+
static void
-get_model_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data);
+
+static gboolean
+pin_puk_recheck_again (gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *resp = strip_tag (response->str, GMM_RESP_TAG);
- if (!error)
- mm_callback_info_set_data (info, "card-info-model", g_strdup (resp), g_free);
- else if (!info->error)
- info->error = g_error_copy (error);
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pin_check_timeout = 0;
+ check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
+ return FALSE;
}
static void
-get_manufacturer_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *resp = strip_tag (response->str, GMI_RESP_TAG);
+ MMSerialPort *port;
- if (!error)
- mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (resp), g_free);
- else
- info->error = g_error_copy (error);
-}
+ /* Clear the pin check timeout to ensure that it won't ever get a
+ * stale MMCallbackInfo if the modem got removed. We'll reschedule it here
+ * anyway if needed.
+ */
+ if (info->modem) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
-static void
-get_card_info (MMModem *modem,
- MMModemInfoFn callback,
- gpointer user_data)
-{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
- MMCallbackInfo *info;
+ if (priv->pin_check_timeout)
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
- info = mm_callback_info_new_full (MM_MODEM (modem),
- card_info_invoke,
- G_CALLBACK (callback),
- user_data);
+ /* modem could have been removed before we get here, in which case
+ * 'modem' will be NULL.
+ */
+ info->error = mm_modem_check_removed (modem, error);
+
+ /* If the modem wasn't removed, and the modem isn't ready yet, ask it for
+ * the current PIN status a few times since some devices take a bit to fully
+ * enable themselves after a SIM PIN/PUK unlock.
+ */
+ if ( info->modem
+ && info->error
+ && !g_error_matches (info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- mm_serial_port_queue_command_cached (priv->primary, "+CGMI", 3, get_manufacturer_done, info);
- mm_serial_port_queue_command_cached (priv->primary, "+CGMM", 3, get_model_done, info);
- mm_serial_port_queue_command_cached (priv->primary, "+CGMR", 3, get_version_done, info);
+ if (priv->pin_check_tries < 4) {
+ g_clear_error (&info->error);
+ priv->pin_check_tries++;
+ priv->pin_check_timeout = g_timeout_add_seconds (2, pin_puk_recheck_again, info);
+ return;
+ }
+ }
+
+ /* Otherwise, clean up and return the PIN check result */
+ port = mm_callback_info_get_data (info, PIN_PORT_TAG);
+ if (modem && port)
+ mm_serial_port_close (port);
+
+ mm_callback_info_schedule (info);
}
static void
-send_puk_done (MMSerialPort *port,
+send_puk_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error)
+ if (error) {
info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
+ mm_callback_info_schedule (info);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ }
+
+ /* Get latest PIN status */
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pin_check_tries = 0;
+ check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
}
static void
@@ -767,27 +1459,52 @@ send_puk (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
+ MMAtSerialPort *port;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ /* Ensure we have a usable port to use for the unlock */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ mm_callback_info_set_data (info, PIN_PORT_TAG, port, NULL);
+
command = g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin);
- mm_serial_port_queue_command (priv->primary, command, 3, send_puk_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, send_puk_done, info);
g_free (command);
}
static void
-send_pin_done (MMSerialPort *port,
+send_pin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error)
+ if (error) {
info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
+ mm_callback_info_schedule (info);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ }
+
+ /* Get latest PIN status */
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pin_check_tries = 0;
+ check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
}
static void
@@ -796,18 +1513,36 @@ send_pin (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
+ MMAtSerialPort *port;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ /* Ensure we have a usable port to use for the unlock */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ mm_callback_info_set_data (info, PIN_PORT_TAG, port, NULL);
+
command = g_strdup_printf ("+CPIN=\"%s\"", pin);
- mm_serial_port_queue_command (priv->primary, command, 3, send_pin_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, send_pin_done, info);
g_free (command);
}
static void
-enable_pin_done (MMSerialPort *port,
+enable_pin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -832,12 +1567,12 @@ enable_pin (MMModemGsmCard *modem,
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin);
- mm_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info);
g_free (command);
}
static void
-change_pin_done (MMSerialPort *port,
+change_pin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -862,12 +1597,104 @@ change_pin (MMModemGsmCard *modem,
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin);
- mm_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info);
g_free (command);
}
+static void
+get_unlock_retries (MMModemGsmCard *modem,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ mm_callback_info_set_result (info,
+ GUINT_TO_POINTER (MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED),
+ NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_info_updated (MMGenericGsm *self,
+ gboolean update_rs,
+ MMGenericGsmRegType rs_type,
+ MMModemGsmNetworkRegStatus status,
+ gboolean update_code,
+ const char *oper_code,
+ gboolean update_name,
+ const char *oper_name)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMModemGsmNetworkRegStatus old_status;
+ gboolean changed = FALSE;
+
+ if (update_rs) {
+ g_return_if_fail ( rs_type == MM_GENERIC_GSM_REG_TYPE_CS
+ || rs_type == MM_GENERIC_GSM_REG_TYPE_PS);
+
+ old_status = gsm_reg_status (self);
+ priv->reg_status[rs_type - 1] = status;
+ if (gsm_reg_status (self) != old_status)
+ changed = TRUE;
+ }
+
+ if (update_code) {
+ if (g_strcmp0 (oper_code, priv->oper_code) != 0) {
+ g_free (priv->oper_code);
+ priv->oper_code = g_strdup (oper_code);
+ changed = TRUE;
+ }
+ }
+
+ if (update_name) {
+ if (g_strcmp0 (oper_name, priv->oper_name) != 0) {
+ g_free (priv->oper_name);
+ priv->oper_name = g_strdup (oper_name);
+ changed = TRUE;
+ }
+ }
+
+ if (changed) {
+ mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (self),
+ gsm_reg_status (self),
+ priv->oper_code,
+ priv->oper_name);
+ }
+}
+
+static void
+convert_operator_from_ucs2 (char **operator)
+{
+ const char *p;
+ char *converted;
+ size_t len;
+
+ g_return_if_fail (operator != NULL);
+ g_return_if_fail (*operator != NULL);
+
+ p = *operator;
+ len = strlen (p);
+
+ /* Len needs to be a multiple of 4 for UCS2 */
+ if ((len < 4) || ((len % 4) != 0))
+ return;
+
+ while (*p) {
+ if (!isxdigit (*p++))
+ return;
+ }
+
+ converted = mm_modem_charset_hex_to_utf8 (*operator, MM_MODEM_CHARSET_UCS2);
+ if (converted) {
+ g_free (*operator);
+ *operator = converted;
+ }
+}
+
static char *
-parse_operator (const char *reply)
+parse_operator (const char *reply, MMModemCharset cur_charset)
{
char *operator = NULL;
@@ -889,52 +1716,53 @@ parse_operator (const char *reply)
g_regex_unref (r);
}
+ /* Some modems (Option & HSO) return the operator name as a hexadecimal
+ * string of the bytes of the operator name as encoded by the current
+ * character set.
+ */
+ if (operator && (cur_charset == MM_MODEM_CHARSET_UCS2))
+ convert_operator_from_ucs2 (&operator);
+
return operator;
}
static void
-read_operator_code_done (MMSerialPort *port,
+read_operator_code_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data);
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
char *oper;
- if (error)
- return;
-
- oper = parse_operator (response->str);
- if (!oper)
- return;
-
- g_free (priv->oper_code);
- priv->oper_code = oper;
+ if (!error) {
+ oper = parse_operator (response->str, MM_MODEM_CHARSET_UNKNOWN);
+ if (oper) {
+ reg_info_updated (self, FALSE, MM_GENERIC_GSM_REG_TYPE_UNKNOWN, 0,
+ TRUE, oper,
+ FALSE, NULL);
+ }
+ }
}
static void
-read_operator_name_done (MMSerialPort *port,
+read_operator_name_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data);
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
char *oper;
- if (error)
- return;
-
- oper = parse_operator (response->str);
- if (!oper)
- return;
-
- g_free (priv->oper_name);
- priv->oper_name = oper;
-
- mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (user_data),
- priv->reg_status,
- priv->oper_code,
- priv->oper_name);
+ if (!error) {
+ oper = parse_operator (response->str, priv->cur_charset);
+ if (oper) {
+ reg_info_updated (self, FALSE, MM_GENERIC_GSM_REG_TYPE_UNKNOWN, 0,
+ FALSE, NULL,
+ TRUE, oper);
+ }
+ }
}
/* Registration */
@@ -961,8 +1789,93 @@ mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem)
}
}
+static void
+got_signal_quality (MMModem *modem,
+ guint32 quality,
+ GError *error,
+ gpointer user_data)
+{
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (modem), quality);
+}
+
+static void
+roam_disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ g_message ("Disconnected because roaming is not allowed");
+}
+
+static void
+get_reg_act_done (MMModem *modem,
+ guint32 act,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem && !error && act)
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (modem), act);
+}
+
+void
+mm_generic_gsm_set_reg_status (MMGenericGsm *self,
+ MMGenericGsmRegType rs_type,
+ MMModemGsmNetworkRegStatus status)
+{
+ MMGenericGsmPrivate *priv;
+ MMAtSerialPort *port;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ g_return_if_fail ( rs_type == MM_GENERIC_GSM_REG_TYPE_CS
+ || rs_type == MM_GENERIC_GSM_REG_TYPE_PS);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (priv->reg_status[rs_type - 1] == status)
+ return;
+
+ g_debug ("%s registration state changed: %d",
+ (rs_type == MM_GENERIC_GSM_REG_TYPE_CS) ? "CS" : "PS",
+ status);
+ priv->reg_status[rs_type - 1] = status;
+
+ port = mm_generic_gsm_get_best_at_port (self, NULL);
+
+ if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
+ status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
+
+ /* If we're connected and we're not supposed to roam, but the device
+ * just roamed, disconnect the connection to avoid charging the user
+ * loads of money.
+ */
+ if ( (status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)
+ && (mm_modem_get_state (MM_MODEM (self)) == MM_MODEM_STATE_CONNECTED)
+ && (priv->roam_allowed == FALSE)) {
+ mm_modem_disconnect (MM_MODEM (self), roam_disconnect_done, NULL);
+ } else {
+ /* Grab the new operator name and MCC/MNC */
+ if (port) {
+ mm_at_serial_port_queue_command (port, "+COPS=3,2;+COPS?", 3, read_operator_code_done, self);
+ mm_at_serial_port_queue_command (port, "+COPS=3,0;+COPS?", 3, read_operator_name_done, self);
+ }
+
+ /* And update signal quality and access technology */
+ mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (self), got_signal_quality, NULL);
+ if (MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology)
+ MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology (self, get_reg_act_done, NULL);
+ }
+ } else
+ reg_info_updated (self, FALSE, rs_type, 0, TRUE, NULL, TRUE, NULL);
+
+ mm_generic_gsm_update_enabled_state (self, TRUE, MM_MODEM_STATE_REASON_NONE);
+}
+
+/* Returns TRUE if the modem is "done", ie has registered or been denied */
static gboolean
-reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
+reg_status_updated (MMGenericGsm *self,
+ MMGenericGsmRegType rs_type,
+ int new_value,
+ GError **error)
{
MMModemGsmNetworkRegStatus status;
gboolean status_done = FALSE;
@@ -991,7 +1904,7 @@ reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
break;
}
- mm_generic_gsm_set_reg_status (self, status);
+ mm_generic_gsm_set_reg_status (self, rs_type, status);
/* Registration has either completed successfully or completely failed */
switch (status) {
@@ -1022,21 +1935,35 @@ reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
return status_done;
}
+static MMGenericGsmRegType
+cgreg_to_reg_type (gboolean cgreg)
+{
+ return (cgreg ? MM_GENERIC_GSM_REG_TYPE_PS : MM_GENERIC_GSM_REG_TYPE_CS);
+}
+
static void
-reg_state_changed (MMSerialPort *port,
+reg_state_changed (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMGenericGsm *self = MM_GENERIC_GSM (user_data);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- char *str;
- gboolean done;
+ guint32 state = 0, idx;
+ gulong lac = 0, cell_id = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ GError *error = NULL;
- str = g_match_info_fetch (match_info, 1);
- done = reg_status_updated (self, atoi (str), NULL);
- g_free (str);
+ if (!mm_gsm_parse_creg_response (match_info, &state, &lac, &cell_id, &act, &cgreg, &error)) {
+ if (mm_options_debug ()) {
+ g_warning ("%s: error parsing unsolicited registration: %s",
+ __func__,
+ error && error->message ? error->message : "(unknown)");
+ }
+ return;
+ }
- if (done) {
+ if (reg_status_updated (self, cgreg_to_reg_type (cgreg), state, NULL)) {
/* If registration is finished (either registered or failed) but the
* registration query hasn't completed yet, just remove the timeout and
* let the registration query complete.
@@ -1046,6 +1973,14 @@ reg_state_changed (MMSerialPort *port,
priv->pending_reg_id = 0;
}
}
+
+ idx = cgreg ? 1 : 0;
+ priv->lac[idx] = lac;
+ priv->cell_id[idx] = cell_id;
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act));
}
static gboolean
@@ -1072,8 +2007,62 @@ reg_status_again_remove (gpointer data)
g_source_remove (id);
}
+static gboolean
+handle_reg_status_response (MMGenericGsm *self,
+ GString *response,
+ GError **error)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ GMatchInfo *match_info;
+ guint32 status = 0, idx;
+ gulong lac = 0, ci = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ guint i;
+
+ /* Try to match the response */
+ for (i = 0; i < priv->reg_regex->len; i++) {
+ GRegex *r = g_ptr_array_index (priv->reg_regex, i);
+
+ if (g_regex_match (r, response->str, 0, &match_info))
+ break;
+ g_match_info_free (match_info);
+ match_info = NULL;
+ }
+
+ if (!match_info) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Unknown registration status response");
+ return FALSE;
+ }
+
+ /* And parse it */
+ if (!mm_gsm_parse_creg_response (match_info, &status, &lac, &ci, &act, &cgreg, error)) {
+ g_match_info_free (match_info);
+ return FALSE;
+ }
+
+ /* Success; update cached location information */
+ idx = cgreg ? 1 : 0;
+ priv->lac[idx] = lac;
+ priv->cell_id[idx] = ci;
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act));
+
+ if (status >= 0) {
+ /* Update cached registration status */
+ reg_status_updated (self, cgreg_to_reg_type (cgreg), status, NULL);
+ }
+
+ return TRUE;
+}
+
+#define CGREG_TRIED_TAG "cgreg-tried"
+
static void
-get_reg_status_done (MMSerialPort *port,
+get_reg_status_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1081,76 +2070,71 @@ get_reg_status_done (MMSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- int reg_status = -1;
- GRegex *r;
- GMatchInfo *match_info;
- char *tmp;
guint id;
+ MMModemGsmNetworkRegStatus status;
- g_warn_if_fail (info == priv->pending_reg_info);
+ /* This function should only get called during the connect sequence when
+ * polling for registration state, since explicit registration requests
+ * from D-Bus clients are filled from the cached registration state.
+ */
+ g_return_if_fail (info == priv->pending_reg_info);
if (error) {
- info->error = g_error_copy (error);
- goto reg_done;
- }
-
- r = g_regex_new ("\\+CREG:\\s*(\\d+),\\s*(\\d+)",
- G_REGEX_RAW | G_REGEX_OPTIMIZE,
- 0, &info->error);
- if (r) {
- g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error);
- if (g_match_info_matches (match_info)) {
- /* Get reg status */
- tmp = g_match_info_fetch (match_info, 2);
- if (isdigit (tmp[0])) {
- tmp[1] = '\0';
- reg_status = atoi (tmp);
- } else {
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Unknown registration status '%s'",
- tmp);
- }
- g_free (tmp);
+ gboolean cgreg_tried = !!mm_callback_info_get_data (info, CGREG_TRIED_TAG);
+
+ /* If this was a +CREG error, try +CGREG. Some devices (blackberries)
+ * respond to +CREG with an error but return a valid +CGREG response.
+ * So try both. If we get an error from both +CREG and +CGREG, that's
+ * obviously a hard fail.
+ */
+ if (cgreg_tried == FALSE) {
+ mm_callback_info_set_data (info, CGREG_TRIED_TAG, GUINT_TO_POINTER (TRUE), NULL);
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, get_reg_status_done, info);
+ return;
} else {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse the registration status response");
+ info->error = g_error_copy (error);
+ goto reg_done;
}
- g_match_info_free (match_info);
- g_regex_unref (r);
}
- if ( reg_status >= 0
- && !reg_status_updated (self, reg_status, &info->error)
- && priv->pending_reg_info) {
- g_clear_error (&info->error);
+ /* The unsolicited registration state handlers will intercept the CREG
+ * response and update the cached registration state for us, so we usually
+ * just need to check the cached state here.
+ */
+
+ if (strlen (response->str)) {
+ /* But just in case the unsolicited handlers doesn't do it... */
+ if (!handle_reg_status_response (self, response, &info->error))
+ goto reg_done;
+ }
- /* Not registered yet; poll registration status again */
+ status = gsm_reg_status (self);
+ 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) {
+ /* If we're still waiting for automatic registration to complete or
+ * fail, check again in a few seconds.
+ */
id = g_timeout_add_seconds (1, reg_status_again, info);
mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG,
- GUINT_TO_POINTER (id),
- reg_status_again_remove);
+ GUINT_TO_POINTER (id),
+ reg_status_again_remove);
return;
}
reg_done:
- /* This will schedule the callback for us */
+ /* This will schedule the pending registration's the callback for us */
mm_generic_gsm_pending_registration_stop (self);
}
static void
-get_registration_status (MMSerialPort *port, MMCallbackInfo *info)
+get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
-
- g_warn_if_fail (info == priv->pending_reg_info);
-
- mm_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info);
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info);
}
static void
-register_done (MMSerialPort *port,
+register_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1169,7 +2153,9 @@ register_done (MMSerialPort *port,
if (priv->pending_reg_info) {
g_warn_if_fail (info == priv->pending_reg_info);
- /* Ignore errors here, get the actual registration status */
+ /* Don't use cached registration state here since it could be up to
+ * 30 seconds old. Get fresh registration state.
+ */
get_registration_status (port, info);
}
}
@@ -1182,7 +2168,16 @@ registration_timed_out (gpointer data)
g_warn_if_fail (info == priv->pending_reg_info);
- priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
+ /* Clear out circuit-switched registration info... */
+ reg_info_updated (MM_GENERIC_GSM (info->modem),
+ TRUE, MM_GENERIC_GSM_REG_TYPE_CS, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE,
+ TRUE, NULL,
+ TRUE, NULL);
+ /* ... and packet-switched registration info */
+ reg_info_updated (MM_GENERIC_GSM (info->modem),
+ TRUE, MM_GENERIC_GSM_REG_TYPE_PS, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE,
+ TRUE, NULL,
+ TRUE, NULL);
info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (info->modem));
@@ -1190,28 +2185,47 @@ registration_timed_out (gpointer data)
return FALSE;
}
+static gboolean
+reg_is_idle (MMModemGsmNetworkRegStatus status)
+{
+ if ( status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING
+ || status == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING)
+ return FALSE;
+ return TRUE;
+}
+
static void
do_register (MMModemGsmNetwork *modem,
const char *network_id,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
- char *command;
+ char *command = NULL;
/* Clear any previous registration */
- mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ mm_generic_gsm_pending_registration_stop (self);
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
priv->pending_reg_id = g_timeout_add_seconds (60, registration_timed_out, info);
priv->pending_reg_info = info;
- if (network_id)
+ /* If the user sent a specific network to use, lock it in. If no specific
+ * network was given, and the modem is not registered and not searching,
+ * kick it to search for a network. Also do auto registration if the modem
+ * had been set to manual registration last time but now is not.
+ */
+ if (network_id) {
command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id);
- else
+ priv->manual_reg = TRUE;
+ } else if (reg_is_idle (gsm_reg_status (self)) || priv->manual_reg) {
command = g_strdup ("+COPS=0,,");
+ priv->manual_reg = FALSE;
+ }
/* Ref the callback info to ensure it stays alive for register_done() even
* if the timeout triggers and ends registration (which calls the callback
@@ -1231,8 +2245,12 @@ do_register (MMModemGsmNetwork *modem,
* the +COPS response is never received.
*/
mm_callback_info_ref (info);
- mm_serial_port_queue_command (priv->primary, command, 120, register_done, info);
- g_free (command);
+
+ if (command) {
+ mm_at_serial_port_queue_command (priv->primary, command, 120, register_done, info);
+ g_free (command);
+ } else
+ register_done (priv->primary, NULL, NULL, info);
}
static void
@@ -1242,7 +2260,7 @@ gsm_network_reg_info_invoke (MMCallbackInfo *info)
MMModemGsmNetworkRegInfoFn callback = (MMModemGsmNetworkRegInfoFn) info->callback;
callback (MM_MODEM_GSM_NETWORK (info->modem),
- priv->reg_status,
+ gsm_reg_status (MM_GENERIC_GSM (info->modem)),
priv->oper_code,
priv->oper_name,
info->error,
@@ -1260,7 +2278,9 @@ get_registration_info (MMModemGsmNetwork *self,
gsm_network_reg_info_invoke,
G_CALLBACK (callback),
user_data);
-
+ /* Registration info updates are handled internally either by unsolicited
+ * updates or by polling. Thus just return the cached registration state.
+ */
mm_callback_info_schedule (info);
}
@@ -1292,7 +2312,7 @@ mm_generic_gsm_connect_complete (MMGenericGsm *modem,
}
static void
-connect_report_done (MMSerialPort *port,
+connect_report_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1323,7 +2343,7 @@ connect_report_done (MMSerialPort *port,
}
static void
-connect_done (MMSerialPort *port,
+connect_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1335,7 +2355,7 @@ connect_done (MMSerialPort *port,
info->error = g_error_copy (error);
/* Try to get more information why it failed */
priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- mm_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info);
+ mm_at_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info);
} else
mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), NULL, info);
}
@@ -1369,21 +2389,21 @@ connect (MMModem *modem,
} else
command = g_strconcat ("DT", number, NULL);
- mm_serial_port_queue_command (priv->primary, command, 60, connect_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 60, connect_done, info);
g_free (command);
}
static void
-disconnect_flash_done (MMSerialPort *port,
- GError *error,
- gpointer user_data)
+disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemState prev_state;
- info->error = mm_modem_check_removed (info->modem, error);
+ info->error = mm_modem_check_removed (modem, error);
if (info->error) {
- if (info->modem) {
+ if (info->modem && modem) {
/* Reset old state since the operation failed */
prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG));
mm_modem_set_state (MM_MODEM (info->modem),
@@ -1391,10 +2411,11 @@ disconnect_flash_done (MMSerialPort *port,
MM_MODEM_STATE_REASON_NONE);
}
} else {
- MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
mm_port_set_connected (priv->data, FALSE);
+ priv->cid = -1;
mm_generic_gsm_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
}
@@ -1402,16 +2423,138 @@ disconnect_flash_done (MMSerialPort *port,
}
static void
+disconnect_all_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ mm_callback_info_schedule ((MMCallbackInfo *) user_data);
+}
+
+static void
+disconnect_send_cgact (MMAtSerialPort *port,
+ gint cid,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ char *command;
+
+ if (cid >= 0)
+ command = g_strdup_printf ("+CGACT=0,%d", cid);
+ else {
+ /* Disable all PDP contexts */
+ command = g_strdup_printf ("+CGACT=0");
+ }
+
+ mm_at_serial_port_queue_command (port, command, 3, callback, user_data);
+ g_free (command);
+}
+
+#define DISCONNECT_CGACT_DONE_TAG "disconnect-cgact-done"
+
+static void
+disconnect_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ /* Ignore "NO CARRIER" response when modem disconnects and any flash
+ * failures we might encounter. Other errors are hard errors.
+ */
+ if ( !g_error_matches (info->error, MM_MODEM_CONNECT_ERROR, MM_MODEM_CONNECT_ERROR_NO_CARRIER)
+ && !g_error_matches (info->error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ g_clear_error (&info->error);
+ }
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ mm_port_set_connected (priv->data, FALSE);
+
+ /* Don't bother doing the CGACT again if it was done on a secondary port */
+ if (mm_callback_info_get_data (info, DISCONNECT_CGACT_DONE_TAG))
+ disconnect_all_done (MM_AT_SERIAL_PORT (priv->primary), NULL, NULL, info);
+ else {
+ disconnect_send_cgact (MM_AT_SERIAL_PORT (priv->primary),
+ priv->cid,
+ disconnect_all_done,
+ info);
+ }
+}
+
+static void
+disconnect_secondary_cgact_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericGsm *self;
+ MMGenericGsmPrivate *priv;
+
+ if (!info->modem) {
+ info->error = mm_modem_check_removed (info->modem, error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ self = MM_GENERIC_GSM (info->modem);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ /* Now that we've tried deactivating the PDP context on the secondary
+ * port, continue with flashing the primary port.
+ */
+ if (!error)
+ mm_callback_info_set_data (info, DISCONNECT_CGACT_DONE_TAG, GUINT_TO_POINTER (TRUE), NULL);
+
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+}
+
+static void
+real_do_disconnect (MMGenericGsm *self,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ /* If the primary port is connected (with PPP) then try sending the PDP
+ * context deactivation on the secondary port because not all modems will
+ * respond to flashing (since either the modem or the kernel's serial
+ * driver doesn't support it).
+ */
+ if ( mm_port_get_connected (MM_PORT (priv->primary))
+ && priv->secondary
+ && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) {
+ disconnect_send_cgact (MM_AT_SERIAL_PORT (priv->secondary),
+ priv->cid,
+ disconnect_secondary_cgact_done,
+ info);
+ } else {
+ /* Just flash the primary port */
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+ }
+}
+
+static void
disconnect (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
MMModemState state;
- /* First, reset the previously used CID */
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
+ priv->roam_allowed = TRUE;
info = mm_callback_info_new (modem, callback, user_data);
@@ -1423,7 +2566,9 @@ disconnect (MMModem *modem,
NULL);
mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info);
+
+ g_assert (MM_GENERIC_GSM_GET_CLASS (self)->do_disconnect);
+ MM_GENERIC_GSM_GET_CLASS (self)->do_disconnect (self, priv->cid, disconnect_done, info);
}
static void
@@ -1438,7 +2583,7 @@ gsm_network_scan_invoke (MMCallbackInfo *info)
}
static void
-scan_done (MMSerialPort *port,
+scan_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1470,30 +2615,33 @@ scan (MMModemGsmNetwork *modem,
G_CALLBACK (callback),
user_data);
- mm_serial_port_queue_command (priv->primary, "+COPS=?", 120, scan_done, info);
+ mm_at_serial_port_queue_command (priv->primary, "+COPS=?", 120, scan_done, info);
}
/* SetApn */
+#define APN_CID_TAG "generic-gsm-cid"
+
static void
-set_apn_done (MMSerialPort *port,
+set_apn_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error)
- info->error = g_error_copy (error);
- else
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem),
- GPOINTER_TO_UINT (mm_callback_info_get_data (info, "cid")));
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ priv->cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, APN_CID_TAG));
+ }
mm_callback_info_schedule (info);
}
static void
-cid_range_read (MMSerialPort *port,
+cid_range_read (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1503,7 +2651,7 @@ cid_range_read (MMSerialPort *port,
if (error)
info->error = g_error_copy (error);
- else if (g_str_has_prefix (response->str, "+CGDCONT: ")) {
+ else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
GRegex *r;
GMatchInfo *match_info;
@@ -1550,16 +2698,16 @@ cid_range_read (MMSerialPort *port,
const char *apn = (const char *) mm_callback_info_get_data (info, "apn");
char *command;
- mm_callback_info_set_data (info, "cid", GUINT_TO_POINTER (cid), NULL);
+ mm_callback_info_set_data (info, APN_CID_TAG, GINT_TO_POINTER (cid), NULL);
command = g_strdup_printf ("+CGDCONT=%d,\"IP\",\"%s\"", cid, apn);
- mm_serial_port_queue_command (port, command, 3, set_apn_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, set_apn_done, info);
g_free (command);
}
}
static void
-existing_apns_read (MMSerialPort *port,
+existing_apns_read (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1567,9 +2715,10 @@ existing_apns_read (MMSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean found = FALSE;
- if (error)
- info->error = g_error_copy (error);
- else if (g_str_has_prefix (response->str, "+CGDCONT: ")) {
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+ else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
GRegex *r;
GMatchInfo *match_info;
@@ -1592,7 +2741,7 @@ existing_apns_read (MMSerialPort *port,
apn = g_match_info_fetch (match_info, 3);
if (!strcmp (apn, new_apn)) {
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), (guint32) num_cid);
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->cid = num_cid;
found = TRUE;
}
@@ -1620,11 +2769,13 @@ existing_apns_read (MMSerialPort *port,
MM_MODEM_ERROR_GENERAL,
"Could not parse the response");
+done:
if (found || info->error)
mm_callback_info_schedule (info);
- else
+ else {
/* APN not configured on the card. Get the allowed CID range */
- mm_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info);
+ }
}
static void
@@ -1640,31 +2791,96 @@ set_apn (MMModemGsmNetwork *modem,
mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free);
/* Start by searching if the APN is already in card */
- mm_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info);
+ mm_at_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info);
}
/* GetSignalQuality */
+static gboolean
+emit_signal_quality_change (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->signal_quality_id = 0;
+ priv->signal_quality_timestamp = time (NULL);
+ mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), priv->signal_quality);
+ return FALSE;
+}
+
+void
+mm_generic_gsm_update_signal_quality (MMGenericGsm *self, guint32 quality)
+{
+ MMGenericGsmPrivate *priv;
+ guint delay = 0;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+ g_return_if_fail (quality <= 100);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (priv->signal_quality == quality)
+ return;
+
+ priv->signal_quality = quality;
+
+ /* Some modems will send unsolcited signal quality changes quite often,
+ * so rate-limit them to every few seconds. Track the last time we
+ * emitted signal quality so that we send the signal immediately if there
+ * haven't been any updates in a while.
+ */
+ if (!priv->signal_quality_id) {
+ if (priv->signal_quality_timestamp > 0) {
+ time_t curtime;
+ long int diff;
+
+ curtime = time (NULL);
+ diff = curtime - priv->signal_quality_timestamp;
+ if (diff == 0) {
+ /* If the device is sending more than one update per second,
+ * make sure we don't spam clients with signals.
+ */
+ delay = 3;
+ } else if ((diff > 0) && (diff <= 3)) {
+ /* Emitted an update less than 3 seconds ago; schedule an update
+ * 3 seconds after the previous one.
+ */
+ delay = (guint) diff;
+ } else {
+ /* Otherwise, we haven't emitted an update in the last 3 seconds,
+ * or the user turned their clock back, or something like that.
+ */
+ delay = 0;
+ }
+ }
+
+ priv->signal_quality_id = g_timeout_add_seconds (delay,
+ emit_signal_quality_change,
+ self);
+ }
+}
+
static void
-get_signal_quality_done (MMSerialPort *port,
+get_signal_quality_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char *reply = response->str;
+ gboolean parsed = FALSE;
- if (error)
- info->error = g_error_copy (error);
- else if (!strncmp (reply, "+CSQ: ", 6)) {
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ if (!strncmp (reply, "+CSQ: ", 6)) {
/* Got valid reply */
int quality;
int ber;
- reply += 6;
-
- if (sscanf (reply, "%d, %d", &quality, &ber)) {
+ if (sscanf (reply + 6, "%d, %d", &quality, &ber)) {
/* 99 means unknown */
if (quality == 99) {
info->error = g_error_new_literal (MM_MOBILE_ERROR,
@@ -1674,15 +2890,19 @@ get_signal_quality_done (MMSerialPort *port,
/* Normalize the quality */
quality = CLAMP (quality, 0, 31) * 100 / 31;
- priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- priv->signal_quality = quality;
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (info->modem), quality);
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
}
- } else
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Could not parse signal quality results");
+ parsed = TRUE;
+ }
}
+ if (!parsed && !info->error) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse signal quality results");
+ }
+
+done:
mm_callback_info_schedule (info);
}
@@ -1693,24 +2913,362 @@ get_signal_quality (MMModemGsmNetwork *modem,
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
- gboolean connected;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), NULL);
+ if (port)
+ mm_at_serial_port_queue_command (port, "+CSQ", 3, get_signal_quality_done, info);
+ else {
+ /* Use cached signal quality */
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL);
+ mm_callback_info_schedule (info);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMModemGsmAccessTech mm_act;
+ gint etsi_act;
+} ModeEtsi;
+
+static ModeEtsi modes_table[] = {
+ { MM_MODEM_GSM_ACCESS_TECH_GSM, 0 },
+ { MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT, 1 },
+ { MM_MODEM_GSM_ACCESS_TECH_UMTS, 2 },
+ { MM_MODEM_GSM_ACCESS_TECH_EDGE, 3 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSDPA, 4 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSUPA, 5 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSPA, 6 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSPA, 7 }, /* E-UTRAN/LTE => HSPA for now */
+ { MM_MODEM_GSM_ACCESS_TECH_UNKNOWN, -1 },
+};
+
+static MMModemGsmAccessTech
+etsi_act_to_mm_act (gint act)
+{
+ ModeEtsi *iter = &modes_table[0];
+
+ while (iter->mm_act != MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) {
+ if (iter->etsi_act == act)
+ return iter->mm_act;
+ iter++;
+ }
+ return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+}
+
+static void
+_internal_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (modem != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ g_return_if_fail (act >= MM_MODEM_GSM_ACCESS_TECH_UNKNOWN && act <= MM_MODEM_GSM_ACCESS_TECH_LAST);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (act != priv->act) {
+ MMModemDeprecatedMode old_mode;
+
+ priv->act = act;
+ g_object_notify (G_OBJECT (modem), MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+
+ /* Deprecated value */
+ old_mode = mm_modem_gsm_network_act_to_old_mode (act);
+ g_signal_emit_by_name (G_OBJECT (modem), "network-mode", old_mode);
+ }
+}
+
+void
+mm_generic_gsm_update_access_technology (MMGenericGsm *self,
+ MMModemGsmAccessTech act)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ /* For plugins, don't update the access tech when the modem isn't enabled */
+ if (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_ENABLED)
+ _internal_update_access_technology (self, act);
+}
+
+void
+mm_generic_gsm_update_allowed_mode (MMGenericGsm *self,
+ MMModemGsmAllowedMode mode)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (mode != priv->allowed_mode) {
+ priv->allowed_mode = mode;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_NETWORK_ALLOWED_MODE);
+ }
+}
+
+static void
+set_allowed_mode_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ MMModemGsmAllowedMode mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"));
- connected = mm_port_get_connected (MM_PORT (priv->primary));
- if (connected && !priv->secondary) {
- g_message ("Returning saved signal quality %d", priv->signal_quality);
- callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data);
+ mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (info->modem), mode);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMModemGsmNetwork *net,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (net);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ if (!MM_GENERIC_GSM_GET_CLASS (self)->set_allowed_mode) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ } else {
+ mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL);
+ MM_GENERIC_GSM_GET_CLASS (self)->set_allowed_mode (self, mode, set_allowed_mode_done, info);
+ }
+ break;
+ default:
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode.");
+ break;
+ }
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+/* Charset stuff */
+
+static void
+get_charsets_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
return;
}
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command (connected ? priv->secondary : priv->primary, "+CSQ", 3, get_signal_quality_done, info);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ priv->charsets = MM_MODEM_CHARSET_UNKNOWN;
+ if (!mm_gsm_parse_cscs_support_response (response->str, &priv->charsets)) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the supported character sets response");
+ } else
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+
+ 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;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+
+ /* Use cached value if we have one */
+ if (priv->charsets) {
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Otherwise hit up the modem */
+ port = mm_generic_gsm_get_best_at_port (self, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ 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;
+
+ 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;
+ }
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ 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);
+}
+
+MMModemCharset
+mm_generic_gsm_get_charset (MMGenericGsm *self)
+{
+ g_return_val_if_fail (self != NULL, MM_MODEM_CHARSET_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_GSM (self), MM_MODEM_CHARSET_UNKNOWN);
+
+ return MM_GENERIC_GSM_GET_PRIVATE (self)->cur_charset;
}
/*****************************************************************************/
/* MMModemGsmSms interface */
static void
-sms_send_done (MMSerialPort *port,
+sms_send_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1733,39 +3291,29 @@ sms_send (MMModemGsmSms *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv;
MMCallbackInfo *info;
char *command;
- gboolean connected;
- MMSerialPort *port = NULL;
+ MMAtSerialPort *port;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- connected = mm_port_get_connected (MM_PORT (priv->primary));
- if (connected)
- port = priv->secondary;
- else
- port = priv->primary;
-
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
if (!port) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot send SMS while connected");
mm_callback_info_schedule (info);
return;
}
/* FIXME: use the PDU mode instead */
- mm_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL);
command = g_strdup_printf ("+CMGS=\"%s\"\r%s\x1a", number, text);
- mm_serial_port_queue_command (port, command, 10, sms_send_done, info);
+ mm_at_serial_port_queue_command (port, command, 10, sms_send_done, info);
g_free (command);
}
-MMSerialPort *
-mm_generic_gsm_get_port (MMGenericGsm *modem,
- MMPortType ptype)
+MMAtSerialPort *
+mm_generic_gsm_get_at_port (MMGenericGsm *modem,
+ MMPortType ptype)
{
g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), NULL);
g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
@@ -1778,105 +3326,267 @@ mm_generic_gsm_get_port (MMGenericGsm *modem,
return NULL;
}
+MMAtSerialPort *
+mm_generic_gsm_get_best_at_port (MMGenericGsm *self, GError **error)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_GENERIC_GSM (self), NULL);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (!mm_port_get_connected (MM_PORT (priv->primary)))
+ return priv->primary;
+
+ if (!priv->secondary) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot perform this operation while connected");
+ }
+
+ return priv->secondary;
+}
+
/*****************************************************************************/
/* MMModemSimple interface */
typedef enum {
- SIMPLE_STATE_BEGIN = 0,
+ SIMPLE_STATE_CHECK_PIN = 0,
SIMPLE_STATE_ENABLE,
- SIMPLE_STATE_CHECK_PIN,
+ SIMPLE_STATE_ALLOWED_MODE,
SIMPLE_STATE_REGISTER,
SIMPLE_STATE_SET_APN,
SIMPLE_STATE_CONNECT,
SIMPLE_STATE_DONE
} SimpleState;
-static const char *
-simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+/* Looks a value up in the simple connect properties dictionary. If the
+ * requested key is not present in the dict, NULL is returned. If the
+ * requested key is present but is not a string, an error is returned.
+ */
+static gboolean
+simple_get_property (MMCallbackInfo *info,
+ const char *name,
+ GType expected_type,
+ const char **out_str,
+ guint32 *out_num,
+ gboolean *out_bool,
+ GError **error)
{
GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
GValue *value;
+ gint foo;
+
+ g_return_val_if_fail (properties != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ if (out_str)
+ g_return_val_if_fail (*out_str == NULL, FALSE);
value = (GValue *) g_hash_table_lookup (properties, name);
if (!value)
- return NULL;
-
- if (G_VALUE_HOLDS_STRING (value))
- return g_value_get_string (value);
+ return FALSE;
+
+ if ((expected_type == G_TYPE_STRING) && G_VALUE_HOLDS_STRING (value)) {
+ *out_str = g_value_get_string (value);
+ return TRUE;
+ } else if (expected_type == G_TYPE_UINT) {
+ if (G_VALUE_HOLDS_UINT (value)) {
+ *out_num = g_value_get_uint (value);
+ return TRUE;
+ } else if (G_VALUE_HOLDS_INT (value)) {
+ /* handle ints for convenience, but only if they are >= 0 */
+ foo = g_value_get_int (value);
+ if (foo >= 0) {
+ *out_num = (guint) foo;
+ return TRUE;
+ }
+ }
+ } else if (expected_type == G_TYPE_BOOLEAN && G_VALUE_HOLDS_BOOLEAN (value)) {
+ *out_bool = g_value_get_boolean (value);
+ return TRUE;
+ }
g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Invalid property type for '%s': %s (string expected)",
- name, G_VALUE_TYPE_NAME (value));
+ "Invalid property type for '%s': %s (%s expected)",
+ name, G_VALUE_TYPE_NAME (value), g_type_name (expected_type));
- return NULL;
+ return FALSE;
+}
+
+static const char *
+simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+{
+ const char *str = NULL;
+
+ simple_get_property (info, name, G_TYPE_STRING, &str, NULL, NULL, error);
+ return str;
+}
+
+static gboolean
+simple_get_uint_property (MMCallbackInfo *info, const char *name, guint32 *out_val, GError **error)
+{
+ return simple_get_property (info, name, G_TYPE_UINT, NULL, out_val, NULL, error);
+}
+
+static gboolean
+simple_get_bool_property (MMCallbackInfo *info, const char *name, gboolean *out_val, GError **error)
+{
+ return simple_get_property (info, name, G_TYPE_BOOLEAN, NULL, NULL, out_val, error);
+}
+
+static gboolean
+simple_get_allowed_mode (MMCallbackInfo *info,
+ MMModemGsmAllowedMode *out_mode,
+ GError **error)
+{
+ MMModemDeprecatedMode old_mode = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY;
+ MMModemGsmAllowedMode allowed_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ GError *tmp_error = NULL;
+
+ /* check for new allowed mode first */
+ if (simple_get_uint_property (info, "allowed_mode", &allowed_mode, &tmp_error)) {
+ if (allowed_mode > MM_MODEM_GSM_ALLOWED_MODE_LAST) {
+ g_set_error (&tmp_error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid allowed mode %d", old_mode);
+ } else {
+ *out_mode = allowed_mode;
+ return TRUE;
+ }
+ } else if (!tmp_error) {
+ /* and if not, the old allowed mode */
+ if (simple_get_uint_property (info, "network_mode", &old_mode, &tmp_error)) {
+ if (old_mode > MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST) {
+ g_set_error (&tmp_error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid allowed mode %d", old_mode);
+ } else {
+ *out_mode = mm_modem_gsm_network_old_mode_to_allowed (old_mode);
+ return TRUE;
+ }
+ }
+ }
+
+ if (error)
+ *error = tmp_error;
+ return FALSE;
}
static void
simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *str;
+ MMGenericGsmPrivate *priv;
+ const char *str, *unlock = NULL;
SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
- gboolean need_pin = FALSE;
+ SimpleState next_state = state;
+ gboolean done = FALSE;
+ MMModemGsmAllowedMode allowed_mode;
+ gboolean home_only = FALSE;
- if (error) {
- if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_SIM_PIN)) {
- need_pin = TRUE;
- state = SIMPLE_STATE_CHECK_PIN;
- } else {
- info->error = g_error_copy (error);
- goto out;
- }
+ info->error = mm_modem_check_removed (modem, error);
+ if (info->error)
+ goto out;
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+ char *data_device;
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): simple connect state %d",
+ tv.tv_sec, tv.tv_usec, data_device, state);
+ g_free (data_device);
}
switch (state) {
- case SIMPLE_STATE_BEGIN:
- state = SIMPLE_STATE_ENABLE;
- mm_modem_enable (modem, simple_state_machine, info);
- break;
- case SIMPLE_STATE_ENABLE:
- state = SIMPLE_STATE_CHECK_PIN;
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (modem), simple_state_machine, info);
- break;
case SIMPLE_STATE_CHECK_PIN:
- if (need_pin) {
- str = simple_get_string_property (info, "pin", &info->error);
- if (str)
- mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info);
- else
- info->error = g_error_copy (error);
- } else {
- str = simple_get_string_property (info, "network_id", &info->error);
- state = SIMPLE_STATE_REGISTER;
- if (!info->error)
- mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ next_state = SIMPLE_STATE_ENABLE;
+
+ /* If we need a PIN, send it now, but we don't care about SIM-PIN2/SIM-PUK2
+ * since the device is operational without it.
+ */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock && strcmp (unlock, "sim-puk2") && strcmp (unlock, "sim-pin2")) {
+ gboolean success = FALSE;
+
+ if (!strcmp (unlock, "sim-pin")) {
+ str = simple_get_string_property (info, "pin", &info->error);
+ if (str) {
+ mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info);
+ success = TRUE;
+ }
+ }
+ if (!success && !info->error)
+ info->error = error_for_unlock_required (unlock);
+ break;
}
+ /* Fall through if no PIN required */
+ case SIMPLE_STATE_ENABLE:
+ next_state = SIMPLE_STATE_ALLOWED_MODE;
+ mm_modem_enable (modem, simple_state_machine, info);
break;
+ case SIMPLE_STATE_ALLOWED_MODE:
+ next_state = SIMPLE_STATE_REGISTER;
+ if ( simple_get_allowed_mode (info, &allowed_mode, &info->error)
+ && (allowed_mode != priv->allowed_mode)) {
+ mm_modem_gsm_network_set_allowed_mode (MM_MODEM_GSM_NETWORK (modem),
+ allowed_mode,
+ simple_state_machine,
+ info);
+ break;
+ } else if (info->error)
+ break;
+ /* otherwise fall through as no allowed mode was sent */
case SIMPLE_STATE_REGISTER:
+ next_state = SIMPLE_STATE_SET_APN;
+ str = simple_get_string_property (info, "network_id", &info->error);
+ if (info->error)
+ str = NULL;
+ mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ break;
+ case SIMPLE_STATE_SET_APN:
+ next_state = SIMPLE_STATE_CONNECT;
str = simple_get_string_property (info, "apn", &info->error);
- if (str) {
- state = SIMPLE_STATE_SET_APN;
- mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ if (str || info->error) {
+ if (str)
+ mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
break;
}
- /* Fall through */
- case SIMPLE_STATE_SET_APN:
- str = simple_get_string_property (info, "number", &info->error);
- state = SIMPLE_STATE_CONNECT;
- mm_modem_connect (modem, str, simple_state_machine, info);
- break;
+ /* Fall through if no APN or no 'apn' property error */
case SIMPLE_STATE_CONNECT:
- state = SIMPLE_STATE_DONE;
+ next_state = SIMPLE_STATE_DONE;
+ str = simple_get_string_property (info, "number", &info->error);
+ if (!info->error) {
+ if (simple_get_bool_property (info, "home_only", &home_only, &info->error)) {
+ MMModemGsmNetworkRegStatus status;
+
+ priv->roam_allowed = !home_only;
+
+ /* Don't connect if we're not supposed to be roaming */
+ status = gsm_reg_status (MM_GENERIC_GSM (modem));
+ if (home_only && (status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)) {
+ info->error = g_error_new_literal (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED,
+ "Roaming is not allowed.");
+ break;
+ }
+ } else if (info->error)
+ break;
+
+ mm_modem_connect (modem, str, simple_state_machine, info);
+ }
break;
case SIMPLE_STATE_DONE:
+ done = TRUE;
break;
}
out:
- if (info->error || state == SIMPLE_STATE_DONE)
+ if (info->error || done)
mm_callback_info_schedule (info);
else
- mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL);
+ mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (next_state), NULL);
}
static void
@@ -1887,6 +3597,29 @@ simple_connect (MMModemSimple *simple,
{
MMCallbackInfo *info;
+ /* If debugging, list all the simple connect properties */
+ if (mm_options_debug ()) {
+ GHashTableIter iter;
+ gpointer key, value;
+ GTimeVal tv;
+ char *data_device;
+
+ g_object_get (G_OBJECT (simple), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_get_current_time (&tv);
+
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ char *val_str;
+
+ val_str = g_strdup_value_contents ((GValue *) value);
+ g_debug ("<%ld.%ld> (%s): %s => %s",
+ tv.tv_sec, tv.tv_usec,
+ data_device, (const char *) key, val_str);
+ g_free (val_str);
+ }
+ g_free (data_device);
+ }
+
info = mm_callback_info_new (MM_MODEM (simple), callback, user_data);
mm_callback_info_set_data (info, "simple-connect-properties",
g_hash_table_ref (properties),
@@ -1895,8 +3628,6 @@ simple_connect (MMModemSimple *simple,
simple_state_machine (MM_MODEM (simple), NULL, info);
}
-
-
static void
simple_free_gvalue (gpointer data)
{
@@ -1928,16 +3659,22 @@ simple_string_value (const char *str)
return val;
}
+#define SS_HASH_TAG "simple-get-status"
+
static void
simple_status_got_signal_quality (MMModem *modem,
guint32 result,
GError *error,
gpointer user_data)
{
- if (error)
- g_warning ("Error getting signal quality: %s", error->message);
- else
- g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result));
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ if (!error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "signal_quality", simple_uint_value (result));
+ }
+ mm_callback_info_chain_complete_one (info);
}
static void
@@ -1946,20 +3683,14 @@ simple_status_got_band (MMModem *modem,
GError *error,
gpointer user_data)
{
- /* Ignore band errors since there's no generic implementation for it */
- if (!error)
- g_hash_table_insert ((GHashTable *) user_data, "band", simple_uint_value (result));
-}
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
-static void
-simple_status_got_mode (MMModem *modem,
- guint32 result,
- GError *error,
- gpointer user_data)
-{
- /* Ignore network mode errors since there's no generic implementation for it */
- if (!error)
- g_hash_table_insert ((GHashTable *) user_data, "network_mode", simple_uint_value (result));
+ if (!error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "band", simple_uint_value (result));
+ }
+ mm_callback_info_chain_complete_one (info);
}
static void
@@ -1973,17 +3704,15 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
GHashTable *properties;
- if (error)
- info->error = g_error_copy (error);
- else {
- properties = (GHashTable *) mm_callback_info_get_data (info, "simple-get-status");
-
+ info->error = mm_modem_check_removed ((MMModem *) modem, error);
+ if (!info->error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+
g_hash_table_insert (properties, "registration_status", simple_uint_value (status));
g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code));
g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name));
}
-
- mm_callback_info_schedule (info);
+ mm_callback_info_chain_complete_one (info);
}
static void
@@ -1992,7 +3721,7 @@ simple_get_status_invoke (MMCallbackInfo *info)
MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback;
callback (MM_MODEM_SIMPLE (info->modem),
- (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"),
+ (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG),
info->error, info->user_data);
}
@@ -2001,23 +3730,54 @@ simple_get_status (MMModemSimple *simple,
MMModemSimpleGetStatusFn callback,
gpointer user_data)
{
- MMModemGsmNetwork *gsm;
+ MMModemGsmNetwork *gsm = MM_MODEM_GSM_NETWORK (simple);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (simple);
GHashTable *properties;
MMCallbackInfo *info;
+ MMModemDeprecatedMode old_mode;
info = mm_callback_info_new_full (MM_MODEM (simple),
simple_get_status_invoke,
G_CALLBACK (callback),
user_data);
- properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue);
- mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref);
+ properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue);
+ mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref);
+
+ mm_callback_info_chain_start (info, 3);
+ mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, info);
+ mm_modem_gsm_network_get_band (gsm, simple_status_got_band, info);
+ mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, info);
+
+ if (priv->act > -1) {
+ /* Deprecated key */
+ old_mode = mm_modem_gsm_network_act_to_old_mode (priv->act);
+ g_hash_table_insert (properties, "network_mode", simple_uint_value (old_mode));
+
+ /* New key */
+ g_hash_table_insert (properties, "access_technology", simple_uint_value (priv->act));
+ }
+}
+
+/*****************************************************************************/
+
+static void
+modem_state_changed (MMGenericGsm *self, GParamSpec *pspec, gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMModemState state;
- gsm = MM_MODEM_GSM_NETWORK (simple);
- mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, properties);
- mm_modem_gsm_network_get_band (gsm, simple_status_got_band, properties);
- mm_modem_gsm_network_get_mode (gsm, simple_status_got_mode, properties);
- mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, properties);
+ /* Start polling registration status and signal quality when enabled */
+
+ state = mm_modem_get_state (MM_MODEM (self));
+ if (state >= MM_MODEM_STATE_ENABLED) {
+ if (!priv->poll_id)
+ priv->poll_id = g_timeout_add_seconds (30, periodic_poll_cb, self);
+ } else {
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
}
/*****************************************************************************/
@@ -2033,6 +3793,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
@@ -2040,10 +3802,12 @@ modem_gsm_card_init (MMModemGsmCard *class)
{
class->get_imei = get_imei;
class->get_imsi = get_imsi;
+ class->get_operator_id = get_operator_id;
class->send_pin = send_pin;
class->send_puk = send_puk;
class->enable_pin = enable_pin;
class->change_pin = change_pin;
+ class->get_unlock_retries = get_unlock_retries;
}
static void
@@ -2051,6 +3815,7 @@ modem_gsm_network_init (MMModemGsmNetwork *class)
{
class->do_register = do_register;
class->get_registration_info = get_registration_info;
+ class->set_allowed_mode = set_allowed_mode;
class->set_apn = set_apn;
class->scan = scan;
class->get_signal_quality = get_signal_quality;
@@ -2072,6 +3837,22 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_gsm_init (MMGenericGsm *self)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ priv->reg_regex = mm_gsm_creg_regex_get (TRUE);
+ priv->roam_allowed = TRUE;
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
+ MM_MODEM_GSM_NETWORK_DBUS_INTERFACE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY,
+ MM_MODEM_GSM_NETWORK_DBUS_INTERFACE);
+
+ g_signal_connect (self, "notify::" MM_MODEM_STATE,
+ G_CALLBACK (modem_state_changed), NULL);
}
static void
@@ -2086,6 +3867,8 @@ set_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL:
case MM_GENERIC_GSM_PROP_SUPPORTED_BANDS:
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
+ case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
+ case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2123,7 +3906,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, "");
break;
case MM_GENERIC_GSM_PROP_INIT_CMD:
- g_value_set_string (value, "Z E0 V1 +CMEE=1");
+ g_value_set_string (value, "Z E0 V1");
break;
case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL:
g_value_set_string (value, "X4 &C1");
@@ -2134,6 +3917,15 @@ get_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
g_value_set_uint (value, 0);
break;
+ case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
+ g_value_set_uint (value, priv->allowed_mode);
+ break;
+ case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
+ if (mm_modem_get_state (MM_MODEM (object)) >= MM_MODEM_STATE_ENABLED)
+ g_value_set_uint (value, priv->act);
+ else
+ g_value_set_uint (value, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2147,6 +3939,23 @@ finalize (GObject *object)
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (object));
+ if (priv->pin_check_timeout) {
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
+
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ if (priv->signal_quality_id) {
+ g_source_remove (priv->signal_quality_id);
+ priv->signal_quality_id = 0;
+ }
+
+ mm_gsm_creg_regex_destroy (priv->reg_regex);
+
g_free (priv->oper_code);
g_free (priv->oper_name);
@@ -2167,6 +3976,8 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
object_class->finalize = finalize;
klass->do_enable = real_do_enable;
+ klass->do_enable_power_up_done = real_do_enable_power_up_done;
+ klass->do_disconnect = real_do_disconnect;
/* Properties */
g_object_class_override_property (object_class,
@@ -2185,6 +3996,14 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
MM_GENERIC_GSM_PROP_SUPPORTED_MODES,
MM_MODEM_GSM_CARD_SUPPORTED_MODES);
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_ALLOWED_MODE,
+ MM_MODEM_GSM_NETWORK_ALLOWED_MODE);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY,
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+
g_object_class_install_property
(object_class, MM_GENERIC_GSM_PROP_POWER_UP_CMD,
g_param_spec_string (MM_GENERIC_GSM_POWER_UP_CMD,
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index de0b00b..de9dc89 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_GENERIC_GSM_H
@@ -20,8 +20,9 @@
#include "mm-modem-gsm.h"
#include "mm-modem-gsm-network.h"
#include "mm-modem-base.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
#include "mm-callback-info.h"
+#include "mm-charsets.h"
#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ())
#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm))
@@ -43,9 +44,16 @@ typedef enum {
MM_GENERIC_GSM_PROP_INIT_CMD,
MM_GENERIC_GSM_PROP_SUPPORTED_BANDS,
MM_GENERIC_GSM_PROP_SUPPORTED_MODES,
- MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL
+ MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL,
+ MM_GENERIC_GSM_PROP_ALLOWED_MODE,
+ MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY
} MMGenericGsmProp;
+typedef enum {
+ MM_GENERIC_GSM_REG_TYPE_UNKNOWN = 0,
+ MM_GENERIC_GSM_REG_TYPE_CS = 1,
+ MM_GENERIC_GSM_REG_TYPE_PS = 2
+} MMGenericGsmRegType;
typedef struct {
MMModemBase parent;
@@ -59,9 +67,49 @@ typedef struct {
* that need to perform custom initialization sequences or other setup should
* generally override this method instead of the MMModem interface's enable()
* method, unless the customization must happen *after* the generic init
- * sequence has completed.
+ * sequence has completed. When the subclass' enable attempt is complete
+ * the subclass should call mm_generic_gsm_enable_complete() with any error
+ * encountered during the process and the MMCallbackInfo created from the
+ * callback and user_data passed in here.
*/
void (*do_enable) (MMGenericGsm *self, MMModemFn callback, gpointer user_data);
+
+ /* Called after the generic class has attempted to power up the modem.
+ * Subclasses can handle errors here if they know the device supports their
+ * power up command. Will only be called if the device does *not* override
+ * the MMModem enable() command or allows the generic class' do_enable()
+ * handler to execute.
+ */
+ void (*do_enable_power_up_done) (MMGenericGsm *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info);
+
+ /* Called to terminate the active data call and deactivate the given PDP
+ * context.
+ */
+ void (*do_disconnect) (MMGenericGsm *self,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to set the allowed operating mode of the device */
+ void (*set_allowed_mode) (MMGenericGsm *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to get the allowed operating mode of the device */
+ void (*get_allowed_mode) (MMGenericGsm *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to the current radio access technology the
+ * device is using while communicating with the base station.
+ */
+ void (*get_access_technology) (MMGenericGsm *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
} MMGenericGsmClass;
GType mm_generic_gsm_get_type (void);
@@ -74,24 +122,42 @@ MMModem *mm_generic_gsm_new (const char *device,
#define MM_GENERIC_GSM_PREV_STATE_TAG "prev-state"
-void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
- gboolean enabled);
-
-void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
+void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
-void mm_generic_gsm_set_cid (MMGenericGsm *modem,
- guint32 cid);
+gint mm_generic_gsm_get_cid (MMGenericGsm *modem);
-guint32 mm_generic_gsm_get_cid (MMGenericGsm *modem);
void mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
+ MMGenericGsmRegType reg_type,
MMModemGsmNetworkRegStatus status);
-void mm_generic_gsm_check_pin (MMGenericGsm *modem,
- MMModemFn callback,
- gpointer user_data);
+MMModemCharset mm_generic_gsm_get_charset (MMGenericGsm *modem);
+
+/* Called to asynchronously update the current allowed operating mode that the
+ * device is allowed to use when connecting to a network. This isn't the
+ * specific access technology the device is currently using (see
+ * mm_generic_gsm_set_access_technology() for that) but the mode the device is
+ * allowed to choose from when connecting.
+ */
+void mm_generic_gsm_update_allowed_mode (MMGenericGsm *modem,
+ MMModemGsmAllowedMode mode);
+
+/* Called to asynchronously update the current access technology of the device;
+ * this is NOT the 2G/3G mode preference, but the current radio access
+ * technology being used to communicate with the base station.
+ */
+void mm_generic_gsm_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act);
+
+/* Called to asynchronously update the current signal quality of the device;
+ * 'quality' is a 0 - 100% quality.
+ */
+void mm_generic_gsm_update_signal_quality (MMGenericGsm *modem, guint32 quality);
+
+MMAtSerialPort *mm_generic_gsm_get_at_port (MMGenericGsm *modem,
+ MMPortType ptype);
-MMSerialPort *mm_generic_gsm_get_port (MMGenericGsm *modem,
- MMPortType ptype);
+MMAtSerialPort *mm_generic_gsm_get_best_at_port (MMGenericGsm *modem,
+ GError **error);
MMPort *mm_generic_gsm_grab_port (MMGenericGsm *modem,
const char *subsys,
diff --git a/src/mm-manager.c b/src/mm-manager.c
index 1a93170..1dd1902 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -11,10 +11,11 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include <string.h>
+#include <ctype.h>
#include <gmodule.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
@@ -52,6 +53,21 @@ typedef struct {
GHashTable *supports;
} MMManagerPrivate;
+typedef struct {
+ MMManager *manager;
+ char *subsys;
+ char *name;
+ char *physdev_path;
+ GSList *plugins;
+ GSList *cur_plugin;
+ guint defer_id;
+ guint done_id;
+
+ guint32 best_level;
+ MMPlugin *best_plugin;
+} SupportsInfo;
+
+
static MMPlugin *
load_plugin (const char *path)
{
@@ -188,44 +204,98 @@ remove_modem (MMManager *manager, MMModem *modem)
}
static void
-modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
+check_export_modem (MMManager *self, MMModem *modem)
{
- MMManager *manager = MM_MANAGER (user_data);
- MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- static guint32 id = 0;
- char *path, *device;
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ char *modem_physdev;
+ GHashTableIter iter;
+ gpointer value;
+
+ /* A modem is only exported to D-Bus when both of the following are true:
+ *
+ * 1) the modem is valid
+ * 2) all ports the modem provides have either been grabbed or are
+ * unsupported by any plugin
+ *
+ * This ensures that all the modem's ports are completely ready before
+ * any clients can do anything with it.
+ *
+ * FIXME: if udev or the kernel are really slow giving us ports, there's a
+ * chance that a port could show up after the modem is already created and
+ * all other ports are already handled. That chance is very small though.
+ */
+ modem_physdev = mm_modem_get_device (modem);
+ g_assert (modem_physdev);
+
+ /* Check for ports that are in the process of being interrogated by plugins */
+ g_hash_table_iter_init (&iter, priv->supports);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ SupportsInfo *info = value;
+
+ if (!strcmp (info->physdev_path, modem_physdev)) {
+ g_debug ("(%s/%s): outstanding support task prevents export of %s",
+ info->subsys, info->name, modem_physdev);
+ goto out;
+ }
+ }
+
+ /* Already exported? This can happen if the modem is exported and the kernel
+ * discovers another of the modem's ports.
+ */
+ if (g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG))
+ goto out;
+
+ /* No outstanding port tasks, so if the modem is valid we can export it */
if (mm_modem_get_valid (modem)) {
+ static guint32 id = 0;
+ char *path, *data_device = NULL;
+
path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++);
dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem));
g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free);
- device = mm_modem_get_device (modem);
- g_assert (device);
- g_debug ("Exported modem %s as %s", device, path);
- g_free (device);
+ g_debug ("Exported modem %s as %s", modem_physdev, path);
- g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
- } else
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_debug ("(%s): data port is %s", path, data_device);
+ g_free (data_device);
+
+ g_signal_emit (self, signals[DEVICE_ADDED], 0, modem);
+ }
+
+out:
+ g_free (modem_physdev);
+}
+
+static void
+modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+ MMManager *manager = MM_MANAGER (user_data);
+
+ if (mm_modem_get_valid (modem))
+ check_export_modem (manager, modem);
+ else
remove_modem (manager, modem);
}
+#define MANAGER_PLUGIN_TAG "manager-plugin"
+
static void
-add_modem (MMManager *manager, MMModem *modem)
+add_modem (MMManager *manager, MMModem *modem, MMPlugin *plugin)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
char *device;
- gboolean valid = FALSE;
device = mm_modem_get_device (modem);
g_assert (device);
if (!g_hash_table_lookup (priv->modems, device)) {
g_hash_table_insert (priv->modems, g_strdup (device), modem);
+ g_object_set_data (G_OBJECT (modem), MANAGER_PLUGIN_TAG, plugin);
+
g_debug ("Added modem %s", device);
- g_signal_connect (modem, "notify::valid", G_CALLBACK (modem_valid), manager);
- g_object_get (modem, MM_MODEM_VALID, &valid, NULL);
- if (valid)
- modem_valid (modem, NULL, manager);
+ g_signal_connect (modem, "notify::" MM_MODEM_VALID, G_CALLBACK (modem_valid), manager);
+ check_export_modem (manager, modem);
}
g_free (device);
}
@@ -236,10 +306,8 @@ enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data)
MMModem *modem = MM_MODEM (val);
GPtrArray **devices = (GPtrArray **) user_data;
const char *path;
- gboolean valid = FALSE;
- g_object_get (G_OBJECT (modem), MM_MODEM_VALID, &valid, NULL);
- if (valid) {
+ if (mm_modem_get_valid (modem)) {
path = g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG);
g_return_if_fail (path != NULL);
g_ptr_array_add (*devices, g_strdup (path));
@@ -265,15 +333,18 @@ find_modem_for_device (MMManager *manager, const char *device)
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
GHashTableIter iter;
gpointer key, value;
+ MMModem *found = NULL;
g_hash_table_iter_init (&iter, priv->modems);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- MMModem *modem = MM_MODEM (value);
+ while (g_hash_table_iter_next (&iter, &key, &value) && !found) {
+ MMModem *candidate = MM_MODEM (value);
+ char *candidate_device = mm_modem_get_device (candidate);
- if (!strcmp (device, mm_modem_get_device (modem)))
- return modem;
+ if (!strcmp (device, candidate_device))
+ found = candidate;
+ g_free (candidate_device);
}
- return NULL;
+ return found;
}
@@ -294,21 +365,11 @@ find_modem_for_port (MMManager *manager, const char *subsys, const char *name)
return NULL;
}
-typedef struct {
- MMManager *manager;
- char *subsys;
- char *name;
- GSList *plugins;
- GSList *cur_plugin;
- guint defer_id;
- guint done_id;
-
- guint32 best_level;
- MMPlugin *best_plugin;
-} SupportsInfo;
-
static SupportsInfo *
-supports_info_new (MMManager *self, const char *subsys, const char *name)
+supports_info_new (MMManager *self,
+ const char *subsys,
+ const char *name,
+ const char *physdev_path)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
SupportsInfo *info;
@@ -317,6 +378,7 @@ supports_info_new (MMManager *self, const char *subsys, const char *name)
info->manager = self;
info->subsys = g_strdup (subsys);
info->name = g_strdup (name);
+ info->physdev_path = g_strdup (physdev_path);
info->plugins = g_slist_copy (priv->plugins);
info->cur_plugin = info->plugins;
return info;
@@ -357,20 +419,21 @@ static void supports_callback (MMPlugin *plugin,
static void try_supports_port (MMManager *manager,
MMPlugin *plugin,
- const char *subsys,
- const char *name,
+ MMModem *existing,
SupportsInfo *info);
static gboolean
supports_defer_timeout (gpointer user_data)
{
SupportsInfo *info = user_data;
+ MMModem *existing;
+
+ existing = find_modem_for_device (info->manager, info->physdev_path);
g_debug ("(%s): re-checking support...", info->name);
try_supports_port (info->manager,
MM_PLUGIN (info->cur_plugin->data),
- info->subsys,
- info->name,
+ existing,
info);
return FALSE;
}
@@ -378,23 +441,30 @@ supports_defer_timeout (gpointer user_data)
static void
try_supports_port (MMManager *manager,
MMPlugin *plugin,
- const char *subsys,
- const char *name,
+ MMModem *existing,
SupportsInfo *info)
{
MMPluginSupportsResult result;
- result = mm_plugin_supports_port (plugin, subsys, name, supports_callback, info);
+ result = mm_plugin_supports_port (plugin,
+ info->subsys,
+ info->name,
+ info->physdev_path,
+ existing,
+ supports_callback,
+ info);
switch (result) {
case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
/* If the plugin knows it doesn't support the modem, just call the
* callback and indicate 0 support.
*/
- supports_callback (plugin, subsys, name, 0, info);
+ supports_callback (plugin, info->subsys, info->name, 0, info);
break;
case MM_PLUGIN_SUPPORTS_PORT_DEFER:
- g_debug ("(%s): (%s) deferring support check", mm_plugin_get_name (plugin), name);
+ g_debug ("(%s): (%s) deferring support check",
+ mm_plugin_get_name (plugin),
+ info->name);
if (info->defer_id)
g_source_remove (info->defer_id);
@@ -407,23 +477,54 @@ try_supports_port (MMManager *manager,
}
}
+static void
+supports_cleanup (MMManager *self,
+ const char *subsys,
+ const char *name,
+ MMModem *modem)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ char *key;
+
+ g_return_if_fail (subsys != NULL);
+ g_return_if_fail (name != NULL);
+
+ key = get_key (subsys, name);
+ g_hash_table_remove (priv->supports, key);
+ g_free (key);
+
+ /* Each time a supports task is cleaned up, check whether the modem is
+ * now completely probed/handled and should be exported to D-Bus clients.
+ *
+ * IMPORTANT: this must be done after removing the supports into from
+ * priv->supports since check_export_modem() searches through priv->supports
+ * for outstanding supports tasks.
+ */
+ if (modem)
+ check_export_modem (self, modem);
+}
+
static gboolean
do_grab_port (gpointer user_data)
{
SupportsInfo *info = user_data;
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager);
- MMModem *modem;
+ MMModem *modem = NULL;
GError *error = NULL;
- char *key;
GSList *iter;
/* No more plugins to try */
if (info->best_plugin) {
+ MMModem *existing;
+
+ existing = g_hash_table_lookup (priv->modems, info->physdev_path);
+
/* Create the modem */
- modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, &error);
+ modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, existing, &error);
if (modem) {
guint32 modem_type = MM_MODEM_TYPE_UNKNOWN;
const char *type_name = "UNKNOWN";
+ char *device;;
g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL);
if (modem_type == MM_MODEM_TYPE_GSM)
@@ -431,13 +532,15 @@ do_grab_port (gpointer user_data)
else if (modem_type == MM_MODEM_TYPE_CDMA)
type_name = "CDMA";
+ device = mm_modem_get_device (modem);
g_message ("(%s): %s modem %s claimed port %s",
mm_plugin_get_name (info->best_plugin),
type_name,
- mm_modem_get_device (modem),
+ device,
info->name);
+ g_free (device);
- add_modem (info->manager, modem);
+ add_modem (info->manager, modem, info->best_plugin);
} else {
g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
__func__,
@@ -446,6 +549,7 @@ do_grab_port (gpointer user_data)
info->name,
error ? error->code : -1,
(error && error->message) ? error->message : "(unknown)");
+ modem = existing;
}
}
@@ -455,10 +559,7 @@ do_grab_port (gpointer user_data)
g_slist_free (info->plugins);
info->cur_plugin = info->plugins = NULL;
- key = get_key (info->subsys, info->name);
- g_hash_table_remove (priv->supports, key);
- g_free (key);
-
+ supports_cleanup (info->manager, info->subsys, info->name, modem);
return FALSE;
}
@@ -471,10 +572,7 @@ supports_callback (MMPlugin *plugin,
{
SupportsInfo *info = user_data;
MMPlugin *next_plugin = NULL;
-
- info->cur_plugin = info->cur_plugin->next;
- if (info->cur_plugin)
- next_plugin = MM_PLUGIN (info->cur_plugin->data);
+ MMModem *existing;
/* Is this plugin's result better than any one we've tried before? */
if (level > info->best_level) {
@@ -482,32 +580,147 @@ supports_callback (MMPlugin *plugin,
info->best_plugin = plugin;
}
- /* Prevent the generic plugin from probing devices that are already supported
- * by other plugins. For Huawei for example, secondary ports shouldn't
- * be probed, but the generic plugin would happily do so if allowed to.
+ /* If there's already a modem for this port's physical device, stop asking
+ * plugins because the same plugin that owns the modem gets this port no
+ * matter what.
*/
- if ( next_plugin
- && !strcmp (mm_plugin_get_name (next_plugin), MM_PLUGIN_GENERIC_NAME)
- && info->best_plugin)
- next_plugin = NULL;
+ existing = find_modem_for_device (info->manager, info->physdev_path);
+ if (existing) {
+ MMPlugin *existing_plugin;
+
+ existing_plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
+ g_assert (existing_plugin);
+
+ if (plugin == existing_plugin) {
+ if (level == 0) {
+ /* If the plugin that just completed the support check claims not to
+ * support this port, but this plugin is clearly the right plugin
+ * since it claimed this port's physical modem, just drop the port.
+ */
+ g_debug ("(%s/%s): ignoring port unsupported by physical modem's plugin",
+ info->subsys, info->name);
+ supports_cleanup (info->manager, info->subsys, info->name, existing);
+ return;
+ }
+
+ /* Otherwise, this port was supported by the plugin that owns the
+ * port's physical modem, so we stop the supports checks anyway.
+ */
+ next_plugin = NULL;
+ } else if (info->best_plugin != existing_plugin) {
+ /* If this port hasn't yet been handled by the right plugin, stop
+ * asking all other plugins if they support this port, just let the
+ * plugin that handles this port's physical device see if it
+ * supports it.
+ */
+ next_plugin = existing_plugin;
+ } else {
+ g_debug ("(%s/%s): plugin %p (%s) existing %p (%s) info->best %p (%s)",
+ info->subsys, info->name,
+ plugin,
+ plugin ? mm_plugin_get_name (plugin) : "none",
+ existing_plugin,
+ existing_plugin ? mm_plugin_get_name (existing_plugin) : "none",
+ info->best_plugin,
+ info->best_plugin ? mm_plugin_get_name (info->best_plugin) : "none");
+ g_assert_not_reached ();
+ }
+ } else {
+ info->cur_plugin = g_slist_next (info->cur_plugin);
+ if (info->cur_plugin)
+ next_plugin = MM_PLUGIN (info->cur_plugin->data);
+ }
+
+ /* Don't bother with Generic if some other plugin already supports this port */
+ if (next_plugin) {
+ const char *next_name = mm_plugin_get_name (next_plugin);
+
+ if (info->best_plugin && !strcmp (next_name, MM_PLUGIN_GENERIC_NAME))
+ next_plugin = NULL;
+ }
if (next_plugin) {
/* Try the next plugin */
- try_supports_port (info->manager, next_plugin, info->subsys, info->name, info);
+ try_supports_port (info->manager, next_plugin, existing, info);
} else {
/* All done; let the best modem grab the port */
info->done_id = g_idle_add (do_grab_port, info);
}
}
+static GUdevDevice *
+find_physical_device (GUdevDevice *child)
+{
+ GUdevDevice *iter, *old = NULL;
+ GUdevDevice *physdev = NULL;
+ const char *subsys, *type;
+ guint32 i = 0;
+ gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
+
+ g_return_val_if_fail (child != NULL, NULL);
+
+ iter = g_object_ref (child);
+ while (iter && i++ < 8) {
+ subsys = g_udev_device_get_subsystem (iter);
+ if (subsys) {
+ if (is_usb || !strcmp (subsys, "usb")) {
+ is_usb = TRUE;
+ type = g_udev_device_get_devtype (iter);
+ if (type && !strcmp (type, "usb_device")) {
+ physdev = iter;
+ break;
+ }
+ } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
+ GUdevDevice *pcmcia_parent;
+ const char *tmp_subsys;
+
+ is_pcmcia = TRUE;
+
+ /* If the parent of this PCMCIA device is no longer part of
+ * the PCMCIA subsystem, we want to stop since we're looking
+ * for the base PCMCIA device, not the PCMCIA controller which
+ * is usually PCI or some other bus type.
+ */
+ pcmcia_parent = g_udev_device_get_parent (iter);
+ if (pcmcia_parent) {
+ tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
+ if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
+ physdev = iter;
+ g_object_unref (pcmcia_parent);
+ if (physdev)
+ break;
+ }
+ } else if (is_platform || !strcmp (subsys, "platform")) {
+ /* Take the first platform parent as the physical device */
+ is_platform = TRUE;
+ physdev = iter;
+ break;
+ } else if (is_pci || !strcmp (subsys, "pci")) {
+ is_pci = TRUE;
+ physdev = iter;
+ break;
+ }
+ }
+
+ old = iter;
+ iter = g_udev_device_get_parent (old);
+ g_object_unref (old);
+ }
+
+ return physdev;
+}
+
static void
device_added (MMManager *manager, GUdevDevice *device)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- const char *subsys, *name;
+ const char *subsys, *name, *physdev_path, *physdev_subsys;
SupportsInfo *info;
char *key;
gboolean found;
+ GUdevDevice *physdev = NULL;
+ MMPlugin *plugin;
+ MMModem *existing;
g_return_if_fail (device != NULL);
@@ -517,20 +730,77 @@ device_added (MMManager *manager, GUdevDevice *device)
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
+ /* ignore VTs */
+ if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
+ return;
+
if (find_modem_for_port (manager, subsys, name))
return;
key = get_key (subsys, name);
found = !!g_hash_table_lookup (priv->supports, key);
- if (found) {
- g_free (key);
- return;
+ if (found)
+ goto out;
+
+ /* Find the port's physical device's sysfs path. This is the kernel device
+ * that "owns" all the ports of the device, like the USB device or the PCI
+ * device the provides each tty or network port.
+ */
+ physdev = find_physical_device (device);
+ if (!physdev) {
+ /* Warn about it, but filter out some common ports that we know don't have
+ * anything to do with mobile broadband.
+ */
+ if ( strcmp (name, "console")
+ && strcmp (name, "ptmx")
+ && strcmp (name, "lo")
+ && strcmp (name, "tty")
+ && !strstr (name, "virbr"))
+ g_debug ("(%s/%s): could not get port's parent device", subsys, name);
+
+ goto out;
+ }
+
+ /* Is the device blacklisted? */
+ if (g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_IGNORE")) {
+ g_debug ("(%s/%s): port's parent device is blacklisted", subsys, name);
+ goto out;
+ }
+
+ /* If the physdev is a 'platform' device that's not whitelisted, ignore it */
+ physdev_subsys = g_udev_device_get_subsystem (physdev);
+ if ( physdev_subsys
+ && !strcmp (physdev_subsys, "platform")
+ && !g_udev_device_get_property_as_boolean (physdev, "ID_MM_PLATFORM_DRIVER_PROBE")) {
+ g_debug ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
+ goto out;
}
- info = supports_info_new (manager, subsys, name);
- g_hash_table_insert (priv->supports, key, info);
+ physdev_path = g_udev_device_get_sysfs_path (physdev);
+ if (!physdev_path) {
+ g_debug ("(%s/%s): could not get port's parent device sysfs path", subsys, name);
+ goto out;
+ }
+
+ /* Success; now ask plugins if they can handle this port */
+ info = supports_info_new (manager, subsys, name, physdev_path);
+ g_hash_table_insert (priv->supports, g_strdup (key), info);
+
+ /* If this port's physical modem is already owned by a plugin, don't bother
+ * asking all plugins whether they support this port, just let the owning
+ * plugin check if it supports the port.
+ */
+ plugin = MM_PLUGIN (info->cur_plugin->data);
+ existing = find_modem_for_device (manager, physdev_path);
+ if (existing)
+ plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
- try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info);
+ try_supports_port (manager, plugin, existing, info);
+
+out:
+ if (physdev)
+ g_object_unref (physdev);
+ g_free (key);
}
static void
@@ -628,12 +898,83 @@ mm_manager_start (MMManager *manager)
priv = MM_MANAGER_GET_PRIVATE (manager);
devices = g_udev_client_query_by_subsystem (priv->udev, "tty");
- for (iter = devices; iter; iter = g_list_next (iter))
+ for (iter = devices; iter; iter = g_list_next (iter)) {
device_added (manager, G_UDEV_DEVICE (iter->data));
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
devices = g_udev_client_query_by_subsystem (priv->udev, "net");
- for (iter = devices; iter; iter = g_list_next (iter))
+ for (iter = devices; iter; iter = g_list_next (iter)) {
device_added (manager, G_UDEV_DEVICE (iter->data));
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
+}
+
+typedef struct {
+ MMManager *manager;
+ MMModem *modem;
+} RemoveInfo;
+
+static gboolean
+remove_disable_one (gpointer user_data)
+{
+ RemoveInfo *info = user_data;
+
+ remove_modem (info->manager, info->modem);
+ g_free (info);
+ return FALSE;
+}
+
+static void
+remove_disable_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ RemoveInfo *info;
+
+ /* Schedule modem removal from an idle handler since we get here deep
+ * in the modem removal callchain and can't remove it quite yet from here.
+ */
+ info = g_malloc0 (sizeof (RemoveInfo));
+ info->manager = MM_MANAGER (user_data);
+ info->modem = modem;
+ g_idle_add (remove_disable_one, info);
+}
+
+void
+mm_manager_shutdown (MMManager *self)
+{
+ GList *modems, *iter;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MANAGER (self));
+
+ modems = g_hash_table_get_values (MM_MANAGER_GET_PRIVATE (self)->modems);
+ for (iter = modems; iter; iter = g_list_next (iter)) {
+ MMModem *modem = MM_MODEM (iter->data);
+
+ if (mm_modem_get_state (modem) >= MM_MODEM_STATE_ENABLING)
+ mm_modem_disable (modem, remove_disable_done, self);
+ else
+ remove_disable_done (modem, NULL, self);
+ }
+ g_list_free (modems);
+
+ /* Disabling may take a few iterations of the mainloop, so the caller
+ * has to iterate the mainloop until all devices have been disabled and
+ * removed.
+ */
+}
+
+guint32
+mm_manager_num_modems (MMManager *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_MANAGER (self), 0);
+
+ return g_hash_table_size (MM_MANAGER_GET_PRIVATE (self)->modems);
}
static void
diff --git a/src/mm-manager.h b/src/mm-manager.h
index 5220b77..1c98458 100644
--- a/src/mm-manager.h
+++ b/src/mm-manager.h
@@ -50,4 +50,8 @@ MMManager *mm_manager_new (DBusGConnection *bus);
void mm_manager_start (MMManager *manager);
+void mm_manager_shutdown (MMManager *manager);
+
+guint32 mm_manager_num_modems (MMManager *manager);
+
#endif /* MM_MANAGER_H */
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index 3d82f8e..0c39d2c 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -21,10 +21,13 @@
#include "mm-modem-base.h"
#include "mm-modem.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-errors.h"
#include "mm-options.h"
#include "mm-properties-changed-signal.h"
+#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
static void modem_init (MMModem *modem_class);
@@ -39,10 +42,19 @@ typedef struct {
char *driver;
char *plugin;
char *device;
+ char *equipment_ident;
+ char *unlock_required;
+ guint32 unlock_retries;
guint32 ip_method;
gboolean valid;
MMModemState state;
+ char *manf;
+ char *model;
+ char *revision;
+
+ MMAuthProvider *authp;
+
GHashTable *ports;
} MMModemBasePrivate;
@@ -92,7 +104,7 @@ mm_modem_base_add_port (MMModemBase *self,
{
MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
MMPort *port = NULL;
- char *key;
+ char *key, *device;
g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
g_return_val_if_fail (subsys != NULL, NULL);
@@ -111,9 +123,12 @@ mm_modem_base_add_port (MMModemBase *self,
g_return_val_if_fail (port == NULL, FALSE);
}
- if (!strcmp (subsys, "tty"))
- port = MM_PORT (mm_serial_port_new (name, ptype));
- else if (!strcmp (subsys, "net")) {
+ if (!strcmp (subsys, "tty")) {
+ if (ptype == MM_PORT_TYPE_QCDM)
+ port = MM_PORT (mm_qcdm_serial_port_new (name, ptype));
+ else
+ port = MM_PORT (mm_at_serial_port_new (name, ptype));
+ } else if (!strcmp (subsys, "net")) {
port = MM_PORT (g_object_new (MM_TYPE_PORT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET,
@@ -124,6 +139,15 @@ mm_modem_base_add_port (MMModemBase *self,
if (!port)
return NULL;
+ if (mm_options_debug ()) {
+ device = mm_modem_get_device (MM_MODEM (self));
+
+ g_message ("(%s) type %s claimed by %s",
+ name,
+ mm_port_type_to_name (ptype),
+ device);
+ g_free (device);
+ }
key = get_hash_key (subsys, name);
g_hash_table_insert (priv->ports, key, port);
return port;
@@ -169,6 +193,362 @@ mm_modem_base_get_valid (MMModemBase *self)
return MM_MODEM_BASE_GET_PRIVATE (self)->valid;
}
+const char *
+mm_modem_base_get_equipment_identifier (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->equipment_ident;
+}
+
+void
+mm_modem_base_set_equipment_identifier (MMModemBase *self, const char *ident)
+{
+ MMModemBasePrivate *priv;
+ const char *dbus_path;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Only do something if the value changes */
+ if ( (priv->equipment_ident == ident)
+ || (priv->equipment_ident && ident && !strcmp (priv->equipment_ident, ident)))
+ return;
+
+ g_free (priv->equipment_ident);
+ priv->equipment_ident = g_strdup (ident);
+
+ dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ if (dbus_path) {
+ if (priv->equipment_ident)
+ g_message ("Modem %s: Equipment identifier set (%s)", dbus_path, priv->equipment_ident);
+ else
+ g_message ("Modem %s: Equipment identifier not set", dbus_path);
+ }
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_EQUIPMENT_IDENTIFIER);
+}
+
+const char *
+mm_modem_base_get_unlock_required (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->unlock_required;
+}
+
+void
+mm_modem_base_set_unlock_required (MMModemBase *self, const char *unlock_required)
+{
+ MMModemBasePrivate *priv;
+ const char *dbus_path;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Only do something if the value changes */
+ if ( (priv->unlock_required == unlock_required)
+ || ( priv->unlock_required
+ && unlock_required
+ && !strcmp (priv->unlock_required, unlock_required)))
+ return;
+
+ g_free (priv->unlock_required);
+ priv->unlock_required = g_strdup (unlock_required);
+
+ dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ if (dbus_path) {
+ if (priv->unlock_required)
+ g_message ("Modem %s: unlock required (%s)", dbus_path, priv->unlock_required);
+ else
+ g_message ("Modem %s: unlock no longer required", dbus_path);
+ }
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_REQUIRED);
+}
+
+guint32
+mm_modem_base_get_unlock_retries (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), 0);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->unlock_retries;
+}
+
+void
+mm_modem_base_set_unlock_retries (MMModemBase *self, guint unlock_retries)
+{
+ MMModemBasePrivate *priv;
+ const char *dbus_path;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Only do something if the value changes */
+ if (priv->unlock_retries == unlock_retries)
+ return;
+
+ priv->unlock_retries = unlock_retries;
+
+ dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ if (dbus_path) {
+ g_message ("Modem %s: # unlock retries for %s is %d",
+ dbus_path, priv->unlock_required, priv->unlock_retries);
+ }
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_RETRIES);
+}
+
+const char *
+mm_modem_base_get_manf (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->manf;
+}
+
+void
+mm_modem_base_set_manf (MMModemBase *self, const char *manf)
+{
+ MMModemBasePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ g_free (priv->manf);
+ priv->manf = g_strdup (manf);
+}
+
+const char *
+mm_modem_base_get_model (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->model;
+}
+
+void
+mm_modem_base_set_model (MMModemBase *self, const char *model)
+{
+ MMModemBasePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ g_free (priv->model);
+ priv->model = g_strdup (model);
+}
+
+const char *
+mm_modem_base_get_revision (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->revision;
+}
+
+void
+mm_modem_base_set_revision (MMModemBase *self, const char *revision)
+{
+ MMModemBasePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ g_free (priv->revision);
+ priv->revision = g_strdup (revision);
+}
+
+/*************************************************************************/
+static void
+card_info_simple_invoke (MMCallbackInfo *info)
+{
+ MMModemBase *self = MM_MODEM_BASE (info->modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ MMModemInfoFn callback = (MMModemInfoFn) info->callback;
+
+ callback (info->modem, priv->manf, priv->model, priv->revision, info->error, info->user_data);
+}
+
+static void
+card_info_cache_invoke (MMCallbackInfo *info)
+{
+ MMModemBase *self = MM_MODEM_BASE (info->modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ MMModemInfoFn callback = (MMModemInfoFn) info->callback;
+ const char *manf, *cmanf, *model, *cmodel, *rev, *crev;
+
+ manf = mm_callback_info_get_data (info, "card-info-manf");
+ cmanf = mm_callback_info_get_data (info, "card-info-c-manf");
+
+ model = mm_callback_info_get_data (info, "card-info-model");
+ cmodel = mm_callback_info_get_data (info, "card-info-c-model");
+
+ rev = mm_callback_info_get_data (info, "card-info-revision");
+ crev = mm_callback_info_get_data (info, "card-info-c-revision");
+
+ /* Prefer the 'C' responses over the plain responses */
+ g_free (priv->manf);
+ priv->manf = g_strdup (cmanf ? cmanf : manf);
+ g_free (priv->model);
+ priv->model = g_strdup (cmodel ? cmodel : model);
+ g_free (priv->revision);
+ priv->revision = g_strdup (crev ? crev : rev);
+
+ callback (info->modem, priv->manf, priv->model, priv->revision, info->error, info->user_data);
+}
+
+static void
+info_item_done (MMCallbackInfo *info,
+ GString *response,
+ GError *error,
+ const char *tag,
+ const char *desc)
+{
+ const char *p;
+
+ if (!error) {
+ p = mm_strip_tag (response->str, tag);
+ mm_callback_info_set_data (info, desc, strlen (p) ? g_strdup (p) : NULL, g_free);
+ }
+
+ mm_callback_info_chain_complete_one (info);
+}
+
+#define GET_INFO_RESP_FN(func_name, tag, desc) \
+static void \
+func_name (MMAtSerialPort *port, \
+ GString *response, \
+ GError *error, \
+ gpointer user_data) \
+{ \
+ info_item_done ((MMCallbackInfo *) user_data, response, error, tag , desc ); \
+}
+
+GET_INFO_RESP_FN(get_revision_done, "+GMR:", "card-info-revision")
+GET_INFO_RESP_FN(get_model_done, "+GMM:", "card-info-model")
+GET_INFO_RESP_FN(get_manf_done, "+GMI:", "card-info-manf")
+
+GET_INFO_RESP_FN(get_c_revision_done, "+CGMR:", "card-info-c-revision")
+GET_INFO_RESP_FN(get_c_model_done, "+CGMM:", "card-info-c-model")
+GET_INFO_RESP_FN(get_c_manf_done, "+CGMI:", "card-info-c-manf")
+
+void
+mm_modem_base_get_card_info (MMModemBase *self,
+ MMAtSerialPort *port,
+ GError *port_error,
+ MMModemInfoFn callback,
+ gpointer user_data)
+{
+ MMModemBasePrivate *priv;
+ MMCallbackInfo *info;
+ MMModemState state;
+ gboolean cached = FALSE;
+ GError *error = port_error;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+ g_return_if_fail (port != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (port));
+ g_return_if_fail (callback != NULL);
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Cached info and errors schedule the callback immediately and do
+ * not hit up the card for it's model information.
+ */
+ if (priv->manf || priv->model || priv->revision)
+ cached = TRUE;
+ else {
+ state = mm_modem_get_state (MM_MODEM (self));
+
+ if (port_error)
+ error = g_error_copy (port_error);
+ else if (state < MM_MODEM_STATE_ENABLING) {
+ error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "The modem is not enabled.");
+ }
+ }
+
+ /* If we have cached info or an error, don't hit up the card */
+ if (cached || error) {
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ card_info_simple_invoke,
+ G_CALLBACK (callback),
+ user_data);
+ info->error = error;
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Otherwise, ask the card */
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ card_info_cache_invoke,
+ G_CALLBACK (callback),
+ user_data);
+
+ mm_callback_info_chain_start (info, 6);
+ mm_at_serial_port_queue_command_cached (port, "+GMI", 3, get_manf_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+GMM", 3, get_model_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+GMR", 3, get_revision_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGMI", 3, get_c_manf_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGMM", 3, get_c_model_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGMR", 3, get_c_revision_done, info);
+}
+
+/*****************************************************************************/
+
+static gboolean
+modem_auth_request (MMModem *modem,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error)
+{
+ MMModemBase *self = MM_MODEM_BASE (modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ g_assert (priv->authp);
+ return !!mm_auth_provider_request_auth (priv->authp,
+ authorization,
+ G_OBJECT (self),
+ context,
+ callback,
+ callback_data,
+ notify,
+ error);
+}
+
+static gboolean
+modem_auth_finish (MMModem *modem, MMAuthRequest *req, GError **error)
+{
+ if (mm_auth_request_get_result (req) != MM_AUTH_RESULT_AUTHORIZED) {
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_AUTHORIZATION_REQUIRED,
+ "This request requires the '%s' authorization",
+ mm_auth_request_get_authorization (req));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/*****************************************************************************/
static void
@@ -176,16 +556,29 @@ mm_modem_base_init (MMModemBase *self)
{
MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ priv->authp = mm_auth_provider_get ();
+
priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
mm_properties_changed_signal_register_property (G_OBJECT (self),
MM_MODEM_ENABLED,
MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_EQUIPMENT_IDENTIFIER,
+ MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_UNLOCK_REQUIRED,
+ MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_UNLOCK_RETRIES,
+ MM_MODEM_DBUS_INTERFACE);
}
static void
modem_init (MMModem *modem_class)
{
+ modem_class->auth_request = modem_auth_request;
+ modem_class->auth_finish = modem_auth_finish;
}
static gboolean
@@ -227,6 +620,9 @@ set_property (GObject *object, guint prop_id,
case MM_MODEM_PROP_VALID:
case MM_MODEM_PROP_TYPE:
case MM_MODEM_PROP_ENABLED:
+ case MM_MODEM_PROP_EQUIPMENT_IDENTIFIER:
+ case MM_MODEM_PROP_UNLOCK_REQUIRED:
+ case MM_MODEM_PROP_UNLOCK_RETRIES:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -268,6 +664,15 @@ get_property (GObject *object, guint prop_id,
case MM_MODEM_PROP_ENABLED:
g_value_set_boolean (value, is_enabled (priv->state));
break;
+ case MM_MODEM_PROP_EQUIPMENT_IDENTIFIER:
+ g_value_set_string (value, priv->equipment_ident);
+ break;
+ case MM_MODEM_PROP_UNLOCK_REQUIRED:
+ g_value_set_string (value, priv->unlock_required);
+ break;
+ case MM_MODEM_PROP_UNLOCK_RETRIES:
+ g_value_set_uint (value, priv->unlock_retries);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -280,10 +685,14 @@ finalize (GObject *object)
MMModemBase *self = MM_MODEM_BASE (object);
MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ mm_auth_provider_cancel_for_owner (priv->authp, object);
+
g_hash_table_destroy (priv->ports);
g_free (priv->driver);
g_free (priv->plugin);
g_free (priv->device);
+ g_free (priv->equipment_ident);
+ g_free (priv->unlock_required);
G_OBJECT_CLASS (mm_modem_base_parent_class)->finalize (object);
}
@@ -336,6 +745,18 @@ mm_modem_base_class_init (MMModemBaseClass *klass)
MM_MODEM_PROP_ENABLED,
MM_MODEM_ENABLED);
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_EQUIPMENT_IDENTIFIER,
+ MM_MODEM_EQUIPMENT_IDENTIFIER);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_UNLOCK_REQUIRED,
+ MM_MODEM_UNLOCK_REQUIRED);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_UNLOCK_RETRIES,
+ MM_MODEM_UNLOCK_RETRIES);
+
mm_properties_changed_signal_new (object_class);
}
diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h
index 9eb64ce..0409957 100644
--- a/src/mm-modem-base.h
+++ b/src/mm-modem-base.h
@@ -22,6 +22,8 @@
#include <glib-object.h>
#include "mm-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-modem.h"
#define MM_TYPE_MODEM_BASE (mm_modem_base_get_type ())
#define MM_MODEM_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_BASE, MMModemBase))
@@ -60,5 +62,36 @@ void mm_modem_base_set_valid (MMModemBase *self,
gboolean mm_modem_base_get_valid (MMModemBase *self);
+const char *mm_modem_base_get_equipment_identifier (MMModemBase *self);
+
+void mm_modem_base_set_equipment_identifier (MMModemBase *self,
+ const char *ident);
+
+const char *mm_modem_base_get_unlock_required (MMModemBase *self);
+
+void mm_modem_base_set_unlock_required (MMModemBase *self,
+ const char *unlock_required);
+
+guint mm_modem_base_get_unlock_retries (MMModemBase *self);
+
+void mm_modem_base_set_unlock_retries (MMModemBase *self,
+ guint unlock_retries);
+
+
+const char *mm_modem_base_get_manf (MMModemBase *self);
+void mm_modem_base_set_manf (MMModemBase *self, const char *manf);
+
+const char *mm_modem_base_get_model (MMModemBase *self);
+void mm_modem_base_set_model (MMModemBase *self, const char *model);
+
+const char *mm_modem_base_get_revision (MMModemBase *self);
+void mm_modem_base_set_revision (MMModemBase *self, const char *revision);
+
+void mm_modem_base_get_card_info (MMModemBase *self,
+ MMAtSerialPort *port,
+ GError *port_error,
+ MMModemInfoFn callback,
+ gpointer user_data);
+
#endif /* MM_MODEM_BASE_H */
diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c
index 112b93f..e80dc4e 100644
--- a/src/mm-modem-cdma.c
+++ b/src/mm-modem-cdma.c
@@ -20,6 +20,7 @@
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-marshal.h"
+#include "mm-auth-provider.h"
static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context);
static void impl_modem_cdma_get_esn (MMModemCdma *modem, DBusGMethodInvocation *context);
@@ -188,10 +189,38 @@ mm_modem_cdma_get_esn (MMModemCdma *self,
}
static void
-impl_modem_cdma_get_esn (MMModemCdma *modem,
- DBusGMethodInvocation *context)
+esn_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- mm_modem_cdma_get_esn (modem, str_call_done, context);
+ MMModemCdma *self = MM_MODEM_CDMA (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the ESN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_cdma_get_esn (self, str_call_done, context);
+}
+
+static void
+impl_modem_cdma_get_esn (MMModemCdma *self, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the ESN */
+ if (!mm_modem_auth_request (MM_MODEM (self),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ esn_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
void
diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c
index dea4590..e03b964 100644
--- a/src/mm-modem-gsm-card.c
+++ b/src/mm-modem-gsm-card.c
@@ -15,6 +15,7 @@
*/
#include <dbus/dbus-glib.h>
+#include <string.h>
#include "mm-modem-gsm-card.h"
#include "mm-errors.h"
@@ -27,6 +28,9 @@ static void impl_gsm_modem_get_imei (MMModemGsmCard *modem,
static void impl_gsm_modem_get_imsi (MMModemGsmCard *modem,
DBusGMethodInvocation *context);
+static void impl_gsm_modem_get_operator_id (MMModemGsmCard *modem,
+ DBusGMethodInvocation *context);
+
static void impl_gsm_modem_send_pin (MMModemGsmCard *modem,
const char *pin,
DBusGMethodInvocation *context);
@@ -76,6 +80,19 @@ str_call_not_supported (MMModemGsmCard *self,
}
static void
+uint_call_not_supported (MMModemGsmCard *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ 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);
+}
+
+static void
async_call_done (MMModem *modem, GError *error, gpointer user_data)
{
DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
@@ -129,6 +146,35 @@ mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
str_call_not_supported (self, callback, user_data);
}
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_GSM_CARD (self));
+ g_return_if_fail (pin_type != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries)
+ MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries (self, pin_type, callback, user_data);
+ else
+ uint_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_gsm_card_get_operator_id (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_GSM_CARD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_operator_id)
+ MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_operator_id (self, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+}
+
void
mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
const char *puk,
@@ -201,26 +247,213 @@ mm_modem_gsm_card_change_pin (MMModemGsmCard *self,
/*****************************************************************************/
static void
-impl_gsm_modem_get_imei (MMModemGsmCard *modem,
- DBusGMethodInvocation *context)
+imei_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- mm_modem_gsm_card_get_imei (modem, str_call_done, context);
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMEI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_get_imei (self, str_call_done, context);
+}
+
+static void
+impl_gsm_modem_get_imei (MMModemGsmCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the IMEI */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ imei_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+imsi_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMSI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_get_imsi (self, str_call_done, context);
+}
+
+static void
+impl_gsm_modem_get_imsi (MMModemGsmCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the IMSI */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ imsi_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+operator_id_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the operator id */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_get_operator_id (self, str_call_done, context);
}
static void
-impl_gsm_modem_get_imsi (MMModemGsmCard *modem,
+impl_gsm_modem_get_operator_id (MMModemGsmCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the operator id */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ operator_id_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ char *puk;
+ char *pin;
+ char *pin2;
+ gboolean enabled;
+} SendPinPukInfo;
+
+static void
+send_pin_puk_info_destroy (gpointer data)
+{
+ SendPinPukInfo *info = data;
+
+ g_free (info->puk);
+ g_free (info->pin);
+ g_free (info->pin2);
+ memset (info, 0, sizeof (SendPinPukInfo));
+ g_free (info);
+}
+
+static SendPinPukInfo *
+send_pin_puk_info_new (const char *puk,
+ const char *pin,
+ const char *pin2,
+ gboolean enabled)
+{
+ SendPinPukInfo *info;
+
+ info = g_malloc0 (sizeof (SendPinPukInfo));
+ info->puk = g_strdup (puk);
+ info->pin = g_strdup (pin);
+ info->pin2 = g_strdup (pin2);
+ info->enabled = enabled;
+ return info;
+}
+
+/*****************************************************************************/
+
+static void
+send_puk_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise send the PUK */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_send_puk (self, info->puk, info->pin, async_call_done, context);
+}
+
+static void
+impl_gsm_modem_send_puk (MMModemGsmCard *modem,
+ const char *puk,
+ const char *pin,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_get_imsi (modem, str_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (puk, pin, NULL, FALSE);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ send_puk_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
+/*****************************************************************************/
+
static void
- impl_gsm_modem_send_puk (MMModemGsmCard *modem,
- const char *puk,
- const char *pin,
- DBusGMethodInvocation *context)
+send_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- mm_modem_gsm_card_send_puk (modem, puk, pin, async_call_done, context);
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise unlock the modem */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_send_pin (self, info->pin, async_call_done, context);
}
static void
@@ -228,7 +461,42 @@ impl_gsm_modem_send_pin (MMModemGsmCard *modem,
const char *pin,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_send_pin (modem, pin, async_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (NULL, pin, NULL, FALSE);
+
+ /* Make sure the caller is authorized to unlock the modem */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ send_pin_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+enable_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise enable the PIN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_enable_pin (self, info->pin, info->enabled, async_call_done, context);
}
static void
@@ -237,7 +505,42 @@ impl_gsm_modem_enable_pin (MMModemGsmCard *modem,
gboolean enabled,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_enable_pin (modem, pin, enabled, async_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (NULL, pin, NULL, enabled);
+
+ /* Make sure the caller is authorized to enable a PIN */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ enable_pin_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+change_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise change the PIN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_change_pin (self, info->pin, info->pin2, async_call_done, context);
}
static void
@@ -246,7 +549,22 @@ impl_gsm_modem_change_pin (MMModemGsmCard *modem,
const char *new_pin,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_change_pin (modem, old_pin, new_pin, async_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (NULL, old_pin, new_pin, FALSE);
+
+ /* Make sure the caller is authorized to change the PIN */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ change_pin_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
/*****************************************************************************/
@@ -267,7 +585,7 @@ mm_modem_gsm_card_init (gpointer g_iface)
"Supported Modes",
"Supported frequency bands of the card",
MM_MODEM_GSM_BAND_UNKNOWN,
- MM_MODEM_GSM_BAND_LAST,
+ G_MAXUINT32,
MM_MODEM_GSM_BAND_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
@@ -277,7 +595,7 @@ mm_modem_gsm_card_init (gpointer g_iface)
"Supported Modes",
"Supported modes of the card (ex 2G preferred, 3G preferred, 2G only, etc",
MM_MODEM_GSM_MODE_UNKNOWN,
- MM_MODEM_GSM_MODE_LAST,
+ G_MAXUINT32,
MM_MODEM_GSM_MODE_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
@@ -305,6 +623,7 @@ mm_modem_gsm_card_get_type (void)
&card_info, 0);
g_type_interface_add_prerequisite (card_type, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (card_type, MM_TYPE_MODEM);
dbus_g_object_type_install_info (card_type, &dbus_glib_mm_modem_gsm_card_object_info);
}
diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h
index 4d690e6..584d734 100644
--- a/src/mm-modem-gsm-card.h
+++ b/src/mm-modem-gsm-card.h
@@ -27,6 +27,13 @@
#define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands"
#define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes"
+#define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin"
+#define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2"
+#define MM_MODEM_GSM_CARD_SIM_PUK "sim-puk"
+#define MM_MODEM_GSM_CARD_SIM_PUK2 "sim-puk2"
+
+#define MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED 999
+
typedef struct _MMModemGsmCard MMModemGsmCard;
struct _MMModemGsmCard {
@@ -41,6 +48,15 @@ struct _MMModemGsmCard {
MMModemStringFn callback,
gpointer user_data);
+ void (*get_unlock_retries) (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*get_operator_id) (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
void (*send_puk) (MMModemGsmCard *self,
const char *puk,
const char *pin,
@@ -75,6 +91,15 @@ void mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
MMModemStringFn callback,
gpointer user_data);
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_gsm_card_get_operator_id (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
void mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
const char *puk,
const char *pin,
diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c
index 26ff422..4cd6921 100644
--- a/src/mm-modem-gsm-network.c
+++ b/src/mm-modem-gsm-network.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2010 Red Hat, Inc.
*/
#include <string.h>
@@ -42,8 +43,12 @@ static void impl_gsm_modem_set_band (MMModemGsmNetwork *modem,
static void impl_gsm_modem_get_band (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context);
+static void impl_gsm_modem_set_allowed_mode (MMModemGsmNetwork *modem,
+ MMModemGsmAllowedMode mode,
+ DBusGMethodInvocation *context);
+
static void impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+ MMModemDeprecatedMode old_mode,
DBusGMethodInvocation *context);
static void impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem,
@@ -68,6 +73,47 @@ static guint signals[LAST_SIGNAL] = { 0 };
/*****************************************************************************/
+MMModemGsmAllowedMode
+mm_modem_gsm_network_old_mode_to_allowed (MMModemDeprecatedMode old_mode)
+{
+ /* Translate deprecated mode into new mode */
+ switch (old_mode) {
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED:
+ return MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED:
+ return MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY:
+ return MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY:
+ return MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY:
+ default:
+ return MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ }
+}
+
+MMModemDeprecatedMode
+mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act)
+{
+ /* Translate new mode into old deprecated mode */
+ if (act & MM_MODEM_GSM_ACCESS_TECH_GPRS)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_EDGE)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_UMTS)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSDPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSUPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA;
+
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY;
+}
+
+/*****************************************************************************/
+
static void
async_call_done (MMModem *modem, GError *error, gpointer user_data)
{
@@ -305,35 +351,21 @@ mm_modem_gsm_network_get_band (MMModemGsmNetwork *self,
}
void
-mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data)
+mm_modem_gsm_network_set_allowed_mode (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
g_return_if_fail (callback != NULL);
- if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode)
- MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode (self, mode, callback, user_data);
+ if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_allowed_mode)
+ MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_allowed_mode (self, mode, callback, user_data);
else
async_call_not_supported (self, callback, user_data);
}
void
-mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data)
-{
- g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
- g_return_if_fail (callback != NULL);
-
- if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode)
- MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode (self, callback, user_data);
- else
- uint_call_not_supported (self, callback, user_data);
-}
-
-void
mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data)
@@ -369,15 +401,6 @@ mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self,
oper_name ? oper_name : "");
}
-void
-mm_modem_gsm_network_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode)
-{
- g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
-
- g_signal_emit (self, signals[NETWORK_MODE], 0, mode);
-}
-
/*****************************************************************************/
static void
@@ -398,10 +421,39 @@ impl_gsm_modem_register (MMModemGsmNetwork *modem,
}
static void
+scan_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmNetwork *self = MM_MODEM_GSM_NETWORK (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMEI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_network_scan (self, scan_call_done, context);
+}
+
+static void
impl_gsm_modem_scan (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_network_scan (modem, scan_call_done, context);
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to request a scan */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ scan_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
static void
@@ -464,10 +516,10 @@ impl_gsm_modem_get_band (MMModemGsmNetwork *modem,
static void
impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+ MMModemDeprecatedMode old_mode,
DBusGMethodInvocation *context)
{
- if (!check_for_single_value (mode)) {
+ if (!check_for_single_value (old_mode)) {
GError *error;
error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
@@ -477,14 +529,41 @@ impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
return;
}
- mm_modem_gsm_network_set_mode (modem, mode, async_call_done, context);
+ mm_modem_gsm_network_set_allowed_mode (modem,
+ mm_modem_gsm_network_old_mode_to_allowed (old_mode),
+ async_call_done,
+ context);
+}
+
+static void
+impl_gsm_modem_set_allowed_mode (MMModemGsmNetwork *modem,
+ MMModemGsmAllowedMode mode,
+ DBusGMethodInvocation *context)
+{
+ if (mode > MM_MODEM_GSM_ALLOWED_MODE_LAST) {
+ GError *error;
+
+ error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Unknown allowed mode %d", mode);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ mm_modem_gsm_network_set_allowed_mode (modem, mode, async_call_done, context);
}
static void
impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_network_get_mode (modem, uint_call_done, context);
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+
+ /* DEPRECATED; it's now a property so it's quite easy to handle */
+ g_object_get (G_OBJECT (modem),
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY, &act,
+ NULL);
+ dbus_g_method_return (context, mm_modem_gsm_network_act_to_old_mode (act));
}
static void
@@ -505,6 +584,28 @@ mm_modem_gsm_network_init (gpointer g_iface)
if (initialized)
return;
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
+ "Allowed Mode",
+ "Allowed network access mode",
+ MM_MODEM_GSM_ALLOWED_MODE_ANY,
+ MM_MODEM_GSM_ALLOWED_MODE_LAST,
+ MM_MODEM_GSM_ALLOWED_MODE_ANY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY,
+ "Access Technology",
+ "Current access technology in use when connected to "
+ "a mobile network.",
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN,
+ MM_MODEM_GSM_ACCESS_TECH_LAST,
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
/* Signals */
signals[SIGNAL_QUALITY] =
g_signal_new ("signal-quality",
@@ -530,8 +631,7 @@ mm_modem_gsm_network_init (gpointer g_iface)
g_signal_new ("network-mode",
iface_type,
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (MMModemGsmNetwork, network_mode),
- NULL, NULL,
+ 0, NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
@@ -562,6 +662,7 @@ mm_modem_gsm_network_get_type (void)
&network_info, 0);
g_type_interface_add_prerequisite (network_type, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (network_type, MM_TYPE_MODEM);
dbus_g_object_type_install_info (network_type, &dbus_glib_mm_modem_gsm_network_object_info);
}
diff --git a/src/mm-modem-gsm-network.h b/src/mm-modem-gsm-network.h
index 493baec..a515789 100644
--- a/src/mm-modem-gsm-network.h
+++ b/src/mm-modem-gsm-network.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_MODEM_GSM_NETWORK_H
@@ -20,11 +20,23 @@
#include <mm-modem.h>
#include <mm-modem-gsm.h>
+#define MM_MODEM_GSM_NETWORK_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Gsm.Network"
+
#define MM_TYPE_MODEM_GSM_NETWORK (mm_modem_gsm_network_get_type ())
#define MM_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork))
#define MM_IS_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_NETWORK))
#define MM_MODEM_GSM_NETWORK_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork))
+#define MM_MODEM_GSM_NETWORK_ALLOWED_MODE "allowed-mode"
+#define MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY "access-technology"
+
+typedef enum {
+ MM_MODEM_GSM_NETWORK_PROP_FIRST = 0x1200,
+
+ MM_MODEM_GSM_NETWORK_PROP_ALLOWED_MODE = MM_MODEM_GSM_NETWORK_PROP_FIRST,
+ MM_MODEM_GSM_NETWORK_PROP_ACCESS_TECHNOLOGY,
+} MMModemGsmNetworkProp;
+
typedef enum {
MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE = 0,
MM_MODEM_GSM_NETWORK_REG_STATUS_HOME = 1,
@@ -80,15 +92,11 @@ struct _MMModemGsmNetwork {
MMModemUIntFn callback,
gpointer user_data);
- void (*set_network_mode) (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
+ void (*set_allowed_mode) (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
MMModemFn callback,
gpointer user_data);
- void (*get_network_mode) (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data);
-
void (*get_registration_info) (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data);
@@ -101,9 +109,6 @@ struct _MMModemGsmNetwork {
MMModemGsmNetworkRegStatus status,
const char *open_code,
const char *oper_name);
-
- void (*network_mode) (MMModemGsmNetwork *self,
- MMModemGsmMode mode);
};
GType mm_modem_gsm_network_get_type (void);
@@ -135,15 +140,6 @@ void mm_modem_gsm_network_get_band (MMModemGsmNetwork *self,
MMModemUIntFn callback,
gpointer user_data);
-void mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data);
-
-void mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data);
-
void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data);
@@ -153,12 +149,19 @@ void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
void mm_modem_gsm_network_signal_quality (MMModemGsmNetwork *self,
guint32 quality);
+void mm_modem_gsm_network_set_allowed_mode (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
void mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegStatus status,
const char *oper_code,
const char *oper_name);
-void mm_modem_gsm_network_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode);
+/* Private */
+MMModemDeprecatedMode mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act);
+
+MMModemGsmAllowedMode mm_modem_gsm_network_old_mode_to_allowed (MMModemDeprecatedMode old_mode);
#endif /* MM_MODEM_GSM_NETWORK_H */
diff --git a/src/mm-modem-gsm-sms.c b/src/mm-modem-gsm-sms.c
index e8ec074..d74c7b3 100644
--- a/src/mm-modem-gsm-sms.c
+++ b/src/mm-modem-gsm-sms.c
@@ -129,12 +129,138 @@ mm_modem_gsm_sms_send (MMModemGsmSms *self,
/*****************************************************************************/
+typedef struct {
+ guint num1;
+ guint num2;
+ guint num3;
+ guint num4;
+ guint num5;
+ char *str;
+ GHashTable *hash;
+} SmsAuthInfo;
+
+static void
+sms_auth_info_destroy (gpointer data)
+{
+ SmsAuthInfo *info = data;
+
+ g_hash_table_destroy (info->hash);
+ g_free (info->str);
+ memset (info, 0, sizeof (SmsAuthInfo));
+ g_free (info);
+}
+
+static void
+destroy_gvalue (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static SmsAuthInfo *
+sms_auth_info_new (guint num1,
+ guint num2,
+ guint num3,
+ guint num4,
+ guint num5,
+ const char *str,
+ GHashTable *hash)
+{
+ SmsAuthInfo *info;
+
+ info = g_malloc0 (sizeof (SmsAuthInfo));
+ info->num1 = num1;
+ info->num2 = num2;
+ info->num3 = num3;
+ info->num4 = num4;
+ info->num5 = num5;
+ info->str = g_strdup (str);
+
+ /* Copy the hash table if we're given one */
+ if (hash) {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ info->hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, destroy_gvalue);
+
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const char *str_key = (const char *) key;
+ GValue *src = (GValue *) value;
+ GValue *dst;
+
+ dst = g_slice_new0 (GValue);
+ g_value_init (dst, G_VALUE_TYPE (src));
+ g_value_copy (src, dst);
+ g_hash_table_insert (info->hash, g_strdup (str_key), dst);
+ }
+ }
+
+ return info;
+}
+
+/*****************************************************************************/
+
+static void
+sms_delete_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
+}
+
static void
impl_gsm_modem_sms_delete (MMModemGsmSms *modem,
guint idx,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (idx, 0, 0, 0, 0, NULL, NULL);
+
+ /* Make sure the caller is authorized to delete an SMS */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_delete_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_get_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -142,9 +268,26 @@ impl_gsm_modem_sms_get (MMModemGsmSms *modem,
guint idx,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (idx, 0, 0, 0, 0, NULL, NULL);
+
+ /* Make sure the caller is authorized to get an SMS */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_get_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
+/*****************************************************************************/
+
static void
impl_gsm_modem_sms_get_format (MMModemGsmSms *modem,
DBusGMethodInvocation *context)
@@ -167,19 +310,103 @@ impl_gsm_modem_sms_get_smsc (MMModemGsmSms *modem,
async_call_not_supported (modem, async_call_done, context);
}
+/*****************************************************************************/
+
+static void
+sms_set_smsc_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise set the SMS service center */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
+}
+
static void
impl_gsm_modem_sms_set_smsc (MMModemGsmSms *modem,
const char *smsc,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (0, 0, 0, 0, 0, smsc, NULL);
+
+ /* Make sure the caller is authorized to set the SMS service center */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_set_smsc_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_list_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise list SMSs */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
impl_gsm_modem_sms_list (MMModemGsmSms *modem,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to list SMSs */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_list_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_save_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise save the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -187,54 +414,122 @@ impl_gsm_modem_sms_save (MMModemGsmSms *modem,
GHashTable *properties,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (0, 0, 0, 0, 0, NULL, properties);
+
+ /* Make sure the caller is authorized to save the SMS */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_save_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
+/*****************************************************************************/
+
static void
-impl_gsm_modem_sms_send (MMModemGsmSms *modem,
- GHashTable *properties,
- DBusGMethodInvocation *context)
+sms_send_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ SmsAuthInfo *info = user_data;
+ GError *error = NULL;
GValue *value;
const char *number = NULL;
const char *text = NULL ;
const char *smsc = NULL;
- GError *error = NULL;
guint validity = 0;
guint class = 0;
- value = (GValue *) g_hash_table_lookup (properties, "number");
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error))
+ goto done;
+
+ value = (GValue *) g_hash_table_lookup (info->hash, "number");
if (value)
number = g_value_get_string (value);
- value = (GValue *) g_hash_table_lookup (properties, "text");
+ value = (GValue *) g_hash_table_lookup (info->hash, "text");
if (value)
text = g_value_get_string (value);
- value = (GValue *) g_hash_table_lookup (properties, "smsc");
+ value = (GValue *) g_hash_table_lookup (info->hash, "smsc");
if (value)
smsc = g_value_get_string (value);
- value = (GValue *) g_hash_table_lookup (properties, "validity");
+ value = (GValue *) g_hash_table_lookup (info->hash, "validity");
if (value)
validity = g_value_get_uint (value);
- value = (GValue *) g_hash_table_lookup (properties, "class");
+ value = (GValue *) g_hash_table_lookup (info->hash, "class");
if (value)
class = g_value_get_uint (value);
- if (!number)
+ if (!number) {
error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Missing number");
- else if (!text)
+ } else if (!text) {
error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Missing message text");
+ }
+done:
if (error) {
- async_call_done (MM_MODEM (modem), error, context);
+ async_call_done (MM_MODEM (self), error, context);
g_error_free (error);
} else
- mm_modem_gsm_sms_send (modem, number, text, smsc, validity, class, async_call_done, context);
+ mm_modem_gsm_sms_send (self, number, text, smsc, validity, class, async_call_done, context);
+}
+
+static void
+impl_gsm_modem_sms_send (MMModemGsmSms *modem,
+ GHashTable *properties,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (0, 0, 0, 0, 0, NULL, properties);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_send_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_send_from_storage_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -242,7 +537,41 @@ impl_gsm_modem_sms_send_from_storage (MMModemGsmSms *modem,
guint idx,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (idx, 0, 0, 0, 0, NULL, NULL);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_send_from_storage_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_set_indication_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -254,7 +583,22 @@ impl_gsm_modem_sms_set_indication (MMModemGsmSms *modem,
guint bfr,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (mode, mt, bm, ds, bfr, NULL, NULL);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_set_indication_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
/*****************************************************************************/
diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h
index 852ff85..6d9135a 100644
--- a/src/mm-modem-gsm.h
+++ b/src/mm-modem-gsm.h
@@ -30,11 +30,35 @@ typedef enum {
MM_MODEM_GSM_MODE_3G_ONLY = 0x00000100,
MM_MODEM_GSM_MODE_HSUPA = 0x00000200,
MM_MODEM_GSM_MODE_HSPA = 0x00000400,
-
- MM_MODEM_GSM_MODE_LAST = MM_MODEM_GSM_MODE_HSPA
+ MM_MODEM_GSM_MODE_GSM = 0x00000800,
+ MM_MODEM_GSM_MODE_GSM_COMPACT = 0x00001000,
} MMModemGsmMode;
typedef enum {
+ MM_MODEM_GSM_ALLOWED_MODE_ANY = 0,
+ MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1,
+ MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2,
+ MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY = 3,
+ MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY = 4,
+
+ MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY
+} MMModemGsmAllowedMode;
+
+typedef enum {
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0,
+ MM_MODEM_GSM_ACCESS_TECH_GSM = 1,
+ MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 2,
+ MM_MODEM_GSM_ACCESS_TECH_GPRS = 3,
+ MM_MODEM_GSM_ACCESS_TECH_EDGE = 4, /* GSM w/EGPRS */
+ MM_MODEM_GSM_ACCESS_TECH_UMTS = 5, /* UTRAN */
+ MM_MODEM_GSM_ACCESS_TECH_HSDPA = 6, /* UTRAN w/HSDPA */
+ MM_MODEM_GSM_ACCESS_TECH_HSUPA = 7, /* UTRAN w/HSUPA */
+ MM_MODEM_GSM_ACCESS_TECH_HSPA = 8, /* UTRAN w/HSDPA and HSUPA */
+
+ MM_MODEM_GSM_ACCESS_TECH_LAST = MM_MODEM_GSM_ACCESS_TECH_HSPA
+} MMModemGsmAccessTech;
+
+typedef enum {
MM_MODEM_GSM_BAND_UNKNOWN = 0x00000000,
MM_MODEM_GSM_BAND_ANY = 0x00000001,
MM_MODEM_GSM_BAND_EGSM = 0x00000002, /* 900 MHz */
@@ -48,10 +72,26 @@ typedef enum {
MM_MODEM_GSM_BAND_U850 = 0x00000200, /* WCDMA 3GPP UMTS 850 MHz (Class V) */
MM_MODEM_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */
MM_MODEM_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */
+ MM_MODEM_GSM_BAND_U1900 = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz (Class II) */
- MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U17IX
+ MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U1900
} MMModemGsmBand;
-#endif /* MM_MODEM_GSM_H */
+typedef enum {
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA
+} MMModemDeprecatedMode;
+
+#endif /* MM_MODEM_GSM_H */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 1741b5f..6e76f46 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -14,13 +14,32 @@
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <glib.h>
#include <string.h>
#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
#include "mm-errors.h"
#include "mm-modem-helpers.h"
+const char *
+mm_strip_tag (const char *str, const char *cmd)
+{
+ const char *p = str;
+
+ if (p) {
+ if (!strncmp (p, cmd, strlen (cmd)))
+ p += strlen (cmd);
+ while (isspace (*p))
+ p++;
+ }
+ return p;
+}
+
+/*************************************************************************/
+
static void
save_scan_value (GHashTable *hash, const char *key, GMatchInfo *info, guint32 num)
{
@@ -201,3 +220,586 @@ mm_gsm_destroy_scan_data (gpointer data)
g_ptr_array_free (results, TRUE);
}
+/*************************************************************************/
+
+/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
+#define CREG1 "\\+(CREG|CGREG):\\s*(\\d{1})"
+
+/* +CREG: <n>,<stat> (GSM 07.07 CREG=1 solicited) */
+#define CREG2 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})"
+
+/* +CREG: <stat>,<lac>,<ci> (GSM 07.07 CREG=2 unsolicited) */
+#define CREG3 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+
+/* +CREG: <n>,<stat>,<lac>,<ci> (GSM 07.07 solicited and some CREG=2 unsolicited) */
+#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+
+/* +CREG: <stat>,<lac>,<ci>,<AcT> (ETSI 27.007 CREG=2 unsolicited) */
+#define CREG5 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
+
+/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
+#define CREG6 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
+
+GPtrArray *
+mm_gsm_creg_regex_get (gboolean solicited)
+{
+ GPtrArray *array = g_ptr_array_sized_new (6);
+ GRegex *regex;
+
+ /* #1 */
+ if (solicited)
+ regex = g_regex_new (CREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #2 */
+ if (solicited)
+ regex = g_regex_new (CREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #3 */
+ if (solicited)
+ regex = g_regex_new (CREG3 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG3 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #4 */
+ if (solicited)
+ regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG4 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #5 */
+ if (solicited)
+ regex = g_regex_new (CREG5 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG5 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #6 */
+ if (solicited)
+ regex = g_regex_new (CREG6 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG6 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ return array;
+}
+
+void
+mm_gsm_creg_regex_destroy (GPtrArray *array)
+{
+ g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL);
+ g_ptr_array_free (array, TRUE);
+}
+
+/*************************************************************************/
+
+static gulong
+parse_uint (char *str, int base, glong nmin, glong nmax, gboolean *valid)
+{
+ gulong ret = 0;
+ char *endquote;
+
+ *valid = FALSE;
+ if (!str)
+ return 0;
+
+ /* Strip quotes */
+ if (str[0] == '"')
+ str++;
+ endquote = strchr (str, '"');
+ if (endquote)
+ *endquote = '\0';
+
+ if (strlen (str)) {
+ ret = strtol (str, NULL, base);
+ if ((nmin == nmax) || (ret >= nmin && ret <= nmax))
+ *valid = TRUE;
+ }
+ return *valid ? (guint) ret : 0;
+}
+
+gboolean
+mm_gsm_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_cgreg,
+ GError **error)
+{
+ gboolean success = FALSE, foo;
+ gint n_matches, act = -1;
+ gulong stat = 0, lac = 0, ci = 0;
+ guint istat = 0, ilac = 0, ici = 0, iact = 0;
+ char *str;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (out_reg_state != NULL, FALSE);
+ g_return_val_if_fail (out_lac != NULL, FALSE);
+ g_return_val_if_fail (out_ci != NULL, FALSE);
+ g_return_val_if_fail (out_act != NULL, FALSE);
+ g_return_val_if_fail (out_cgreg != NULL, FALSE);
+
+ str = g_match_info_fetch (info, 1);
+ if (str && strstr (str, "CGREG"))
+ *out_cgreg = TRUE;
+
+ /* Normally the number of matches could be used to determine what each
+ * item is, but we have overlap in one case.
+ */
+ n_matches = g_match_info_get_match_count (info);
+ if (n_matches == 3) {
+ /* CREG=1: +CREG: <stat> */
+ istat = 2;
+ } else if (n_matches == 4) {
+ /* Solicited response: +CREG: <n>,<stat> */
+ istat = 3;
+ } else if (n_matches == 5) {
+ /* CREG=2 (GSM 07.07): +CREG: <stat>,<lac>,<ci> */
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ } else if (n_matches == 6) {
+ /* CREG=2 (ETSI 27.007): +CREG: <stat>,<lac>,<ci>,<AcT>
+ * CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>
+ */
+
+ /* To distinguish, check length of the third match item. If it's
+ * more than one digit or has quotes in it then it's a LAC and we
+ * got the first format.
+ */
+ str = g_match_info_fetch (info, 3);
+ if (str && (strchr (str, '"') || strlen (str) > 1)) {
+ g_free (str);
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ iact = 5;
+ } else {
+ istat = 3;
+ ilac = 4;
+ ici = 5;
+ }
+ } else if (n_matches == 7) {
+ /* CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>,<AcT> */
+ istat = 3;
+ ilac = 4;
+ ici = 5;
+ iact = 6;
+ }
+
+ /* Status */
+ str = g_match_info_fetch (info, istat);
+ stat = parse_uint (str, 10, 0, 5, &success);
+ g_free (str);
+ if (!success) {
+ g_set_error_literal (error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse the registration status response");
+ return FALSE;
+ }
+
+ /* Location Area Code */
+ if (ilac) {
+ /* FIXME: some phones apparently swap the LAC bytes (LG, SonyEricsson,
+ * Sagem). Need to handle that.
+ */
+ str = g_match_info_fetch (info, ilac);
+ lac = parse_uint (str, 16, 1, 0xFFFF, &foo);
+ g_free (str);
+ }
+
+ /* Cell ID */
+ if (ici) {
+ str = g_match_info_fetch (info, ici);
+ ci = parse_uint (str, 16, 1, 0x0FFFFFFE, &foo);
+ g_free (str);
+ }
+
+ /* Access Technology */
+ if (iact) {
+ str = g_match_info_fetch (info, iact);
+ act = (gint) parse_uint (str, 10, 0, 7, &foo);
+ g_free (str);
+ if (!foo)
+ act = -1;
+ }
+
+ *out_reg_state = (guint32) stat;
+ if (stat != 4) {
+ /* Don't fill in lac/ci/act if the device's state is unknown */
+ *out_lac = lac;
+ *out_ci = ci;
+ *out_act = act;
+ }
+ return TRUE;
+}
+
+/*************************************************************************/
+
+gboolean
+mm_cdma_parse_spservice_response (const char *reply,
+ MMModemCdmaRegistrationState *out_cdma_1x_state,
+ MMModemCdmaRegistrationState *out_evdo_state)
+{
+ const char *p;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (out_cdma_1x_state != NULL, FALSE);
+ g_return_val_if_fail (out_evdo_state != NULL, FALSE);
+
+ p = mm_strip_tag (reply, "+SPSERVICE:");
+ if (!isdigit (*p))
+ return FALSE;
+
+ switch (atoi (p)) {
+ case 0: /* no service */
+ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ break;
+ case 1: /* 1xRTT */
+ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ break;
+ case 2: /* EVDO rev 0 */
+ case 3: /* EVDO rev A */
+ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*************************************************************************/
+
+typedef struct {
+ int num;
+ gboolean roam_ind;
+ const char *banner;
+} EriItem;
+
+/* NOTE: these may be Sprint-specific for now... */
+static const EriItem eris[] = {
+ { 0, TRUE, "Digital or Analog Roaming" },
+ { 1, FALSE, "Home" },
+ { 2, TRUE, "Digital or Analog Roaming" },
+ { 3, TRUE, "Out of neighborhood" },
+ { 4, TRUE, "Out of building" },
+ { 5, TRUE, "Preferred system" },
+ { 6, TRUE, "Available System" },
+ { 7, TRUE, "Alliance Partner" },
+ { 8, TRUE, "Premium Partner" },
+ { 9, TRUE, "Full Service Functionality" },
+ { 10, TRUE, "Partial Service Functionality" },
+ { 64, TRUE, "Preferred system" },
+ { 65, TRUE, "Available System" },
+ { 66, TRUE, "Alliance Partner" },
+ { 67, TRUE, "Premium Partner" },
+ { 68, TRUE, "Full Service Functionality" },
+ { 69, TRUE, "Partial Service Functionality" },
+ { 70, TRUE, "Analog A" },
+ { 71, TRUE, "Analog B" },
+ { 72, TRUE, "CDMA 800 A" },
+ { 73, TRUE, "CDMA 800 B" },
+ { 74, TRUE, "International Roaming" },
+ { 75, TRUE, "Extended Network" },
+ { 76, FALSE, "Campus" },
+ { 77, FALSE, "In Building" },
+ { 78, TRUE, "Regional" },
+ { 79, TRUE, "Community" },
+ { 80, TRUE, "Business" },
+ { 81, TRUE, "Zone 1" },
+ { 82, TRUE, "Zone 2" },
+ { 83, TRUE, "National" },
+ { 84, TRUE, "Local" },
+ { 85, TRUE, "City" },
+ { 86, TRUE, "Government" },
+ { 87, TRUE, "USA" },
+ { 88, TRUE, "State" },
+ { 89, TRUE, "Resort" },
+ { 90, TRUE, "Headquarters" },
+ { 91, TRUE, "Personal" },
+ { 92, FALSE, "Home" },
+ { 93, TRUE, "Residential" },
+ { 94, TRUE, "University" },
+ { 95, TRUE, "College" },
+ { 96, TRUE, "Hotel Guest" },
+ { 97, TRUE, "Rental" },
+ { 98, FALSE, "Corporate" },
+ { 99, FALSE, "Home Provider" },
+ { 100, FALSE, "Campus" },
+ { 101, FALSE, "In Building" },
+ { 102, TRUE, "Regional" },
+ { 103, TRUE, "Community" },
+ { 104, TRUE, "Business" },
+ { 105, TRUE, "Zone 1" },
+ { 106, TRUE, "Zone 2" },
+ { 107, TRUE, "National" },
+ { 108, TRUE, "Local" },
+ { 109, TRUE, "City" },
+ { 110, TRUE, "Government" },
+ { 111, TRUE, "USA" },
+ { 112, TRUE, "State" },
+ { 113, TRUE, "Resort" },
+ { 114, TRUE, "Headquarters" },
+ { 115, TRUE, "Personal" },
+ { 116, FALSE, "Home" },
+ { 117, TRUE, "Residential" },
+ { 118, TRUE, "University" },
+ { 119, TRUE, "College" },
+ { 120, TRUE, "Hotel Guest" },
+ { 121, TRUE, "Rental" },
+ { 122, FALSE, "Corporate" },
+ { 123, FALSE, "Home Provider" },
+ { 124, TRUE, "International" },
+ { 125, TRUE, "International" },
+ { 126, TRUE, "International" },
+ { 127, FALSE, "Premium Service" },
+ { 128, FALSE, "Enhanced Service" },
+ { 129, FALSE, "Enhanced Digital" },
+ { 130, FALSE, "Enhanced Roaming" },
+ { 131, FALSE, "Alliance Service" },
+ { 132, FALSE, "Alliance Network" },
+ { 133, FALSE, "Data Roaming" }, /* Sprint: Vision Roaming */
+ { 134, FALSE, "Extended Service" },
+ { 135, FALSE, "Expanded Services" },
+ { 136, FALSE, "Expanded Network" },
+ { 137, TRUE, "Premium Service" },
+ { 138, TRUE, "Enhanced Service" },
+ { 139, TRUE, "Enhanced Digital" },
+ { 140, TRUE, "Enhanced Roaming" },
+ { 141, TRUE, "Alliance Service" },
+ { 142, TRUE, "Alliance Network" },
+ { 143, TRUE, "Data Roaming" }, /* Sprint: Vision Roaming */
+ { 144, TRUE, "Extended Service" },
+ { 145, TRUE, "Expanded Services" },
+ { 146, TRUE, "Expanded Network" },
+ { 147, TRUE, "Premium Service" },
+ { 148, TRUE, "Enhanced Service" },
+ { 149, TRUE, "Enhanced Digital" },
+ { 150, TRUE, "Enhanced Roaming" },
+ { 151, TRUE, "Alliance Service" },
+ { 152, TRUE, "Alliance Network" },
+ { 153, TRUE, "Data Roaming" }, /* Sprint: Vision Roaming */
+ { 154, TRUE, "Extended Service" },
+ { 155, TRUE, "Expanded Services" },
+ { 156, TRUE, "Expanded Network" },
+ { 157, TRUE, "Premium International" },
+ { 158, TRUE, "Premium International" },
+ { 159, TRUE, "Premium International" },
+ { 160, TRUE, NULL },
+ { 161, TRUE, NULL },
+ { 162, FALSE, NULL },
+ { 163, FALSE, NULL },
+ { 164, FALSE, "Extended Voice/Data Network" },
+ { 165, FALSE, "Extended Voice/Data Network" },
+ { 166, TRUE, "Extended Voice/Data Network" },
+ { 167, FALSE, "Extended Broadband" },
+ { 168, FALSE, "Extended Broadband" },
+ { 169, TRUE, "Extended Broadband" },
+ { 170, FALSE, "Extended Data" },
+ { 171, FALSE, "Extended Data" },
+ { 172, TRUE, "Extended Data" },
+ { 173, FALSE, "Extended Data Network" },
+ { 174, FALSE, "Extended Data Network" },
+ { 175, TRUE, "Extended Data Network" },
+ { 176, FALSE, "Extended Network" },
+ { 177, FALSE, "Extended Network" },
+ { 178, TRUE, "Extended Network" },
+ { 179, FALSE, "Extended Service" },
+ { 180, TRUE, "Extended Service" },
+ { 181, FALSE, "Extended Voice" },
+ { 182, FALSE, "Extended Voice" },
+ { 183, TRUE, "Extended Voice" },
+ { 184, FALSE, "Extended Voice/Data" },
+ { 185, FALSE, "Extended Voice/Data" },
+ { 186, TRUE, "Extended Voice/Data" },
+ { 187, FALSE, "Extended Voice Network" },
+ { 188, FALSE, "Extended Voice Network" },
+ { 189, TRUE, "Extended Voice Network" },
+ { 190, FALSE, "Extended Voice/Data" },
+ { 191, FALSE, "Extended Voice/Data" },
+ { 192, TRUE, "Extended Voice/Data" },
+ { 193, TRUE, "International" },
+ { 194, FALSE, "International Services" },
+ { 195, FALSE, "International Voice" },
+ { 196, FALSE, "International Voice/Data" },
+ { 197, FALSE, "International Voice/Data" },
+ { 198, TRUE, "International Voice/Data" },
+ { 199, FALSE, "Extended Voice/Data Network" },
+ { 200, TRUE, "Extended Voice/Data Network" },
+ { 201, TRUE, "Extended Voice/Data Network" },
+ { 202, FALSE, "Extended Broadband" },
+ { 203, TRUE, "Extended Broadband" },
+ { 204, TRUE, "Extended Broadband" },
+ { 205, FALSE, "Extended Data" },
+ { 206, TRUE, "Extended Data" },
+ { 207, TRUE, "Extended Data" },
+ { 208, FALSE, "Extended Data Network" },
+ { 209, TRUE, "Extended Data Network" },
+ { 210, TRUE, "Extended Data Network" },
+ { 211, FALSE, "Extended Network" },
+ { 212, TRUE, "Extended Network" },
+ { 213, FALSE, "Extended Service" },
+ { 214, TRUE, "Extended Service" },
+ { 215, TRUE, "Extended Service" },
+ { 216, FALSE, "Extended Voice" },
+ { 217, TRUE, "Extended Voice" },
+ { 218, TRUE, "Extended Voice" },
+ { 219, FALSE, "Extended Voice/Data" },
+ { 220, TRUE, "Extended Voice/Data" },
+ { 221, TRUE, "Extended Voice/Data" },
+ { 222, FALSE, "Extended Voice Network" },
+ { 223, FALSE, "Extended Voice Network" },
+ { 224, TRUE, "Extended Voice Network" },
+ { 225, FALSE, "Extended Voice/Data" },
+ { 226, TRUE, "Extended Voice/Data" },
+ { 227, TRUE, "Extended Voice/Data" },
+ { 228, TRUE, "International" },
+ { 229, TRUE, "International" },
+ { 230, TRUE, "International Services" },
+ { 231, TRUE, "International Voice" },
+ { 232, FALSE, "International Voice/Data" },
+ { 233, TRUE, "International Voice/Data" },
+ { 234, TRUE, "International Voice/Data" },
+ { 235, TRUE, "Premium International" },
+ { 236, TRUE, NULL },
+ { 237, TRUE, NULL },
+ { 238, FALSE, NULL },
+ { 239, FALSE, NULL },
+ { -1, FALSE, NULL },
+};
+
+gboolean
+mm_cdma_parse_eri (const char *reply,
+ gboolean *out_roaming,
+ guint32 *out_ind,
+ const char **out_desc)
+{
+ long int ind;
+ const EriItem *iter = &eris[0];
+ gboolean found = FALSE;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (out_roaming != NULL, FALSE);
+
+ errno = 0;
+ ind = strtol (reply, NULL, 10);
+ if (errno == 0) {
+ if (out_ind)
+ *out_ind = ind;
+
+ while (iter->num != -1) {
+ if (iter->num == ind) {
+ *out_roaming = iter->roam_ind;
+ if (out_desc)
+ *out_desc = iter->banner;
+ found = TRUE;
+ break;
+ }
+ iter++;
+ }
+ }
+
+ return found;
+}
+
+/*************************************************************************/
+
+gboolean
+mm_gsm_parse_cscs_support_response (const char *reply,
+ MMModemCharset *out_charsets)
+{
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ GRegex *r;
+ GMatchInfo *match_info;
+ char *p, *str;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (out_charsets != NULL, FALSE);
+
+ /* Find the first '(' or '"'; the general format is:
+ *
+ * +CSCS: ("IRA","GSM","UCS2")
+ *
+ * but some devices (some Blackberries) don't include the ().
+ */
+ p = strchr (reply, '(');
+ if (p)
+ p++;
+ else {
+ p = strchr (reply, '"');
+ if (!p)
+ return FALSE;
+ }
+
+ /* Now parse each charset */
+ r = g_regex_new ("\\s*([^,\\)]+)\\s*", 0, 0, NULL);
+ if (!r)
+ return FALSE;
+
+ if (g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) {
+ while (g_match_info_matches (match_info)) {
+ str = g_match_info_fetch (match_info, 1);
+ charsets |= mm_modem_charset_from_string (str);
+ g_free (str);
+
+ g_match_info_next (match_info, NULL);
+ success = TRUE;
+ }
+ g_match_info_free (match_info);
+ }
+ g_regex_unref (r);
+
+ if (success)
+ *out_charsets = charsets;
+
+ return success;
+}
+
+/*************************************************************************/
+
+MMModemGsmAccessTech
+mm_gsm_string_to_access_tech (const char *string)
+{
+ g_return_val_if_fail (string != NULL, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+
+ /* Better technologies are listed first since modems sometimes say
+ * stuff like "GPRS/EDGE" and that should be handled as EDGE.
+ */
+ if (strcasestr (string, "HSPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ else if (strcasestr (string, "HSDPA/HSUPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ else if (strcasestr (string, "HSUPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+ else if (strcasestr (string, "HSDPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ else if (strcasestr (string, "UMTS"))
+ return MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ else if (strcasestr (string, "EDGE"))
+ return MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ else if (strcasestr (string, "GPRS"))
+ return MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ else if (strcasestr (string, "GSM"))
+ return MM_MODEM_GSM_ACCESS_TECH_GSM;
+
+ return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+}
+
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index ddc9cbc..fb100bc 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -17,6 +17,10 @@
#ifndef MM_MODEM_HELPERS_H
#define MM_MODEM_HELPERS_H
+#include "mm-modem-cdma.h"
+#include "mm-modem-gsm.h"
+#include "mm-charsets.h"
+
#define MM_SCAN_TAG_STATUS "status"
#define MM_SCAN_TAG_OPER_LONG "operator-long"
#define MM_SCAN_TAG_OPER_SHORT "operator-short"
@@ -27,5 +31,33 @@ GPtrArray *mm_gsm_parse_scan_response (const char *reply, GError **error);
void mm_gsm_destroy_scan_data (gpointer data);
+GPtrArray *mm_gsm_creg_regex_get (gboolean solicited);
+
+void mm_gsm_creg_regex_destroy (GPtrArray *array);
+
+gboolean mm_gsm_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_cgreg,
+ GError **error);
+
+const char *mm_strip_tag (const char *str, const char *cmd);
+
+gboolean mm_cdma_parse_spservice_response (const char *reply,
+ MMModemCdmaRegistrationState *out_cdma_1x_state,
+ MMModemCdmaRegistrationState *out_evdo_state);
+
+gboolean mm_cdma_parse_eri (const char *reply,
+ gboolean *out_roaming,
+ guint32 *out_ind,
+ const char **out_desc);
+
+gboolean mm_gsm_parse_cscs_support_response (const char *reply,
+ MMModemCharset *out_charsets);
+
+MMModemGsmAccessTech mm_gsm_string_to_access_tech (const char *string);
+
#endif /* MM_MODEM_HELPERS_H */
diff --git a/src/mm-modem-location.c b/src/mm-modem-location.c
new file mode 100644
index 0000000..0018295
--- /dev/null
+++ b/src/mm-modem-location.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+
+#include "mm-modem-location.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-marshal.h"
+
+static void impl_modem_location_enable (MMModemLocation *modem,
+ gboolean enable,
+ gboolean signal_location,
+ DBusGMethodInvocation *context);
+
+static void impl_modem_location_get_location (MMModemLocation *modem,
+ DBusGMethodInvocation *context);
+
+#include "mm-modem-location-glue.h"
+
+/*****************************************************************************/
+
+static void
+async_call_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+async_call_not_supported (MMModemLocation *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ 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 {
+ gboolean enable;
+ gboolean signals_location;
+} LocAuthInfo;
+
+static void
+loc_auth_info_destroy (gpointer data)
+{
+ LocAuthInfo *info = data;
+
+ memset (info, 0, sizeof (LocAuthInfo));
+ g_free (info);
+}
+
+static LocAuthInfo *
+loc_auth_info_new (gboolean enable, gboolean signals_location)
+{
+ LocAuthInfo *info;
+
+ info = g_malloc0 (sizeof (LocAuthInfo));
+ info->enable = enable;
+ info->signals_location = signals_location;
+ return info;
+}
+
+/*****************************************************************************/
+
+void
+mm_modem_location_enable (MMModemLocation *self,
+ gboolean enable,
+ gboolean signals_location,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LOCATION (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LOCATION_GET_INTERFACE (self)->enable)
+ MM_MODEM_LOCATION_GET_INTERFACE (self)->enable (self, enable, signals_location, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+loc_enable_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (owner);
+ LocAuthInfo *info = (LocAuthInfo *) user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise enable location gathering */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_location_enable (self, info->enable, info->signals_location, async_call_done, context);
+}
+
+static void
+impl_modem_location_enable (MMModemLocation *modem,
+ gboolean enable,
+ gboolean signals_location,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ LocAuthInfo *info;
+
+ info = loc_auth_info_new (enable, signals_location);
+
+ /* Make sure the caller is authorized to enable location gathering */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_LOCATION,
+ context,
+ loc_enable_auth_cb,
+ info,
+ loc_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+loc_get_invoke (MMCallbackInfo *info)
+{
+ MMModemLocationGetFn callback = (MMModemLocationGetFn) info->callback;
+
+ callback ((MMModemLocation *) info->modem, NULL, info->error, info->user_data);
+}
+
+static void
+async_get_call_not_supported (MMModemLocation *self,
+ MMModemLocationGetFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ loc_get_invoke,
+ G_CALLBACK (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_location_get_location (MMModemLocation *self,
+ MMModemLocationGetFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LOCATION (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LOCATION_GET_INTERFACE (self)->get_location)
+ MM_MODEM_LOCATION_GET_INTERFACE (self)->get_location (self, callback, user_data);
+ else
+ async_get_call_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+async_get_call_done (MMModemLocation *self,
+ GHashTable *locations,
+ GError *error,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, locations);
+}
+
+static void
+loc_get_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the location */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_location_get_location (self, async_get_call_done, context);
+}
+
+static void
+impl_modem_location_get_location (MMModemLocation *modem,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ LocAuthInfo *info;
+
+ info = loc_auth_info_new (FALSE, FALSE);
+
+ /* Make sure the caller is authorized to enable location gathering */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_LOCATION,
+ context,
+ loc_get_auth_cb,
+ info,
+ loc_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_location_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boxed (MM_MODEM_LOCATION_LOCATION,
+ "Location",
+ "Available location information",
+ G_TYPE_HASH_TABLE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_LOCATION_CAPABILITIES,
+ "Capabilities",
+ "Supported location information methods",
+ MM_MODEM_LOCATION_CAPABILITY_UNKNOWN,
+ MM_MODEM_LOCATION_CAPABILITY_GPS_NMEA
+ | MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI,
+ MM_MODEM_LOCATION_CAPABILITY_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_MODEM_LOCATION_ENABLED,
+ "Enabled",
+ "Whether or not location gathering is enabled",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_MODEM_LOCATION_SIGNALS_LOCATION,
+ "SignalsLocation",
+ "Whether or not location updates are emitted as signals",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ initialized = TRUE;
+}
+
+GType
+mm_modem_location_get_type (void)
+{
+ static GType loc_type = 0;
+
+ if (!G_UNLIKELY (loc_type)) {
+ const GTypeInfo loc_info = {
+ sizeof (MMModemLocation), /* class_size */
+ mm_modem_location_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ loc_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMModemLocation",
+ &loc_info, 0);
+
+ g_type_interface_add_prerequisite (loc_type, G_TYPE_OBJECT);
+ dbus_g_object_type_install_info (loc_type, &dbus_glib_mm_modem_location_object_info);
+
+ /* Register some shadow properties to handle Enabled and Capabilities
+ * since these could be used by other interfaces.
+ */
+ dbus_g_object_type_register_shadow_property (loc_type,
+ "Enabled",
+ MM_MODEM_LOCATION_ENABLED);
+ dbus_g_object_type_register_shadow_property (loc_type,
+ "Capabilities",
+ MM_MODEM_LOCATION_CAPABILITIES);
+ }
+
+ return loc_type;
+}
diff --git a/src/mm-modem-location.h b/src/mm-modem-location.h
new file mode 100644
index 0000000..dd622f1
--- /dev/null
+++ b/src/mm-modem-location.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_MODEM_LOCATION_H
+#define MM_MODEM_LOCATION_H
+
+#include <mm-modem.h>
+
+#define MM_TYPE_MODEM_LOCATION (mm_modem_location_get_type ())
+#define MM_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LOCATION, MMModemLocation))
+#define MM_IS_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LOCATION))
+#define MM_MODEM_LOCATION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_LOCATION, MMModemLocation))
+
+#define MM_MODEM_LOCATION_CAPABILITIES "location-capabilities"
+#define MM_MODEM_LOCATION_ENABLED "location-enabled"
+#define MM_MODEM_LOCATION_SIGNALS_LOCATION "signals-location"
+#define MM_MODEM_LOCATION_LOCATION "location"
+
+typedef enum {
+ MM_MODEM_LOCATION_CAPABILITY_UNKNOWN = 0x00000000,
+ MM_MODEM_LOCATION_CAPABILITY_GPS_NMEA = 0x00000001,
+ MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI = 0x00000002,
+
+ MM_MODEM_LOCATION_CAPABILITY_LAST = MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI
+} MMModemLocationCapabilities;
+
+typedef struct _MMModemLocation MMModemLocation;
+
+typedef void (*MMModemLocationGetFn) (MMModemLocation *modem,
+ GHashTable *locations,
+ GError *error,
+ gpointer user_data);
+
+struct _MMModemLocation {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ void (*enable) (MMModemLocation *modem,
+ gboolean enable,
+ gboolean signal_location,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_location) (MMModemLocation *modem,
+ MMModemLocationGetFn callback,
+ gpointer user_data);
+};
+
+GType mm_modem_location_get_type (void);
+
+void mm_modem_location_enable (MMModemLocation *self,
+ gboolean enable,
+ gboolean signal_location,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_location_get_location (MMModemLocation *self,
+ MMModemLocationGetFn callback,
+ gpointer user_data);
+
+#endif /* MM_MODEM_LOCATION_H */
diff --git a/src/mm-modem.c b/src/mm-modem.c
index a65d883..221c9ea 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -14,9 +14,11 @@
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include "mm-modem.h"
+#include "mm-options.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-marshal.h"
@@ -26,6 +28,7 @@ static void impl_modem_connect (MMModem *modem, const char *number, DBusGMethodI
static void impl_modem_disconnect (MMModem *modem, DBusGMethodInvocation *context);
static void impl_modem_get_ip4_config (MMModem *modem, DBusGMethodInvocation *context);
static void impl_modem_get_info (MMModem *modem, DBusGMethodInvocation *context);
+static void impl_modem_factory_reset (MMModem *modem, const char *code, DBusGMethodInvocation *context);
#include "mm-modem-glue.h"
@@ -148,12 +151,15 @@ disable_disconnect_done (MMModem *self,
}
if (error) {
+ char *device = mm_modem_get_device (self);
+
/* Don't really care what the error was; log it and proceed to disable */
g_warning ("%s: (%s): error disconnecting the modem while disabling: (%d) %s",
__func__,
- mm_modem_get_device (self),
+ device,
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
+ g_free (device);
}
finish_disable (self, cb_data->callback, cb_data->user_data);
g_free (cb_data);
@@ -471,6 +477,106 @@ impl_modem_get_info (MMModem *modem,
/*****************************************************************************/
+static void
+factory_reset_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModem *self = MM_MODEM (owner);
+ const char *code = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise try to reset the modem */
+ if (!mm_modem_auth_finish (self, req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_factory_reset (self, code, async_call_done, context);
+}
+
+static void
+impl_modem_factory_reset (MMModem *modem,
+ const char *code,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to reset the device */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ factory_reset_auth_cb,
+ g_strdup (code),
+ g_free,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+void
+mm_modem_factory_reset (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (code != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->factory_reset)
+ MM_MODEM_GET_INTERFACE (self)->factory_reset (self, code, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+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);
+ }
+}
+
+/*****************************************************************************/
+
gboolean
mm_modem_owns_port (MMModem *self,
const char *subsys,
@@ -598,16 +704,76 @@ mm_modem_set_state (MMModem *self,
dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
if (dbus_path) {
- g_message ("Modem %s: state changed (%s -> %s)",
- dbus_path,
- state_to_string (old_state),
- state_to_string (new_state));
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> Modem %s: state changed (%s -> %s)",
+ tv.tv_sec,
+ tv.tv_usec,
+ dbus_path,
+ state_to_string (old_state),
+ state_to_string (new_state));
+ } else {
+ g_message ("Modem %s: state changed (%s -> %s)",
+ dbus_path,
+ state_to_string (old_state),
+ state_to_string (new_state));
+ }
}
}
}
/*****************************************************************************/
+gboolean
+mm_modem_auth_request (MMModem *self,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (authorization != NULL, FALSE);
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ g_return_val_if_fail (MM_MODEM_GET_INTERFACE (self)->auth_request, FALSE);
+ return MM_MODEM_GET_INTERFACE (self)->auth_request (self,
+ authorization,
+ context,
+ callback,
+ callback_data,
+ notify,
+ error);
+}
+
+gboolean
+mm_modem_auth_finish (MMModem *self,
+ MMAuthRequest *req,
+ GError **error)
+{
+ gboolean success;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (req != NULL, FALSE);
+
+ g_return_val_if_fail (MM_MODEM_GET_INTERFACE (self)->auth_finish, FALSE);
+ success = MM_MODEM_GET_INTERFACE (self)->auth_finish (self, req, error);
+
+ /* If the request failed, the implementor *should* return an error */
+ if (!success && error)
+ g_warn_if_fail (*error != NULL);
+
+ return success;
+}
+
+/*****************************************************************************/
+
static void
mm_modem_init (gpointer g_iface)
{
@@ -694,6 +860,31 @@ mm_modem_init (gpointer g_iface)
FALSE,
G_PARAM_READABLE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_EQUIPMENT_IDENTIFIER,
+ "EquipmentIdentifier",
+ "The equipment identifier of the device",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_UNLOCK_REQUIRED,
+ "UnlockRequired",
+ "Whether or not the modem requires an unlock "
+ "code to become usable, and if so, which unlock code is required",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_UNLOCK_RETRIES,
+ "UnlockRetries",
+ "The remaining number of unlock attempts",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE));
+
/* Signals */
g_signal_new ("state-changed",
iface_type,
diff --git a/src/mm-modem.h b/src/mm-modem.h
index e8dd7ea..0915180 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -18,8 +18,11 @@
#define MM_MODEM_H
#include <glib-object.h>
+#include <dbus/dbus-glib-lowlevel.h>
#include "mm-port.h"
+#include "mm-auth-provider.h"
+#include "mm-charsets.h"
typedef enum {
MM_MODEM_STATE_UNKNOWN = 0,
@@ -55,6 +58,9 @@ typedef enum {
#define MM_MODEM_TYPE "type"
#define MM_MODEM_IP_METHOD "ip-method"
#define MM_MODEM_ENABLED "enabled"
+#define MM_MODEM_EQUIPMENT_IDENTIFIER "equipment-identifier"
+#define MM_MODEM_UNLOCK_REQUIRED "unlock-required"
+#define MM_MODEM_UNLOCK_RETRIES "unlock-retries"
#define MM_MODEM_VALID "valid" /* not exported */
#define MM_MODEM_PLUGIN "plugin" /* not exported */
#define MM_MODEM_STATE "state" /* not exported */
@@ -78,7 +84,10 @@ typedef enum {
MM_MODEM_PROP_VALID, /* Not exported */
MM_MODEM_PROP_PLUGIN, /* Not exported */
MM_MODEM_PROP_STATE, /* Not exported */
- MM_MODEM_PROP_ENABLED
+ MM_MODEM_PROP_ENABLED,
+ MM_MODEM_PROP_EQUIPMENT_IDENTIFIER,
+ MM_MODEM_PROP_UNLOCK_REQUIRED,
+ MM_MODEM_PROP_UNLOCK_RETRIES
} MMModemProp;
typedef struct _MMModem MMModem;
@@ -154,6 +163,36 @@ 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.
+ */
+ gboolean (*auth_request) (MMModem *self,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error);
+
+ gboolean (*auth_finish) (MMModem *self,
+ MMAuthRequest *req,
+ GError **error);
+
+ void (*factory_reset) (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data);
+
/* Signals */
void (*state_changed) (MMModem *self,
MMModemState new_state,
@@ -203,6 +242,20 @@ 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);
+
+void mm_modem_factory_reset (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data);
+
gboolean mm_modem_get_valid (MMModem *self);
char *mm_modem_get_device (MMModem *self);
@@ -215,5 +268,21 @@ void mm_modem_set_state (MMModem *self,
GError *mm_modem_check_removed (MMModem *self, const GError *error);
+/* Request authorization to perform an action. Used by D-Bus method
+ * handlers to ensure that the incoming request is authorized to perform
+ * the action it's requesting.
+ */
+gboolean mm_modem_auth_request (MMModem *self,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error);
+
+gboolean mm_modem_auth_finish (MMModem *self,
+ MMAuthRequest *req,
+ GError **error);
+
#endif /* MM_MODEM_H */
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 202eaac..80d0f90 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#define _GNU_SOURCE /* for strcasestr */
@@ -26,10 +26,14 @@
#include <gudev/gudev.h>
#include "mm-plugin-base.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-errors.h"
#include "mm-marshal.h"
+#include "mm-utils.h"
+#include "libqcdm/src/commands.h"
+#include "libqcdm/src/utils.h"
static void plugin_init (MMPlugin *plugin_class);
@@ -48,8 +52,6 @@ static GHashTable *cached_caps = NULL;
typedef struct {
char *name;
GUdevClient *client;
-
- GHashTable *modems;
GHashTable *tasks;
} MMPluginBasePrivate;
@@ -77,6 +79,8 @@ typedef enum {
PROBE_STATE_LAST
} ProbeState;
+static void probe_complete (MMPluginBaseSupportsTask *task);
+
/*****************************************************************************/
G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OBJECT)
@@ -86,13 +90,15 @@ G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OB
typedef struct {
MMPluginBase *plugin;
GUdevDevice *port;
- GUdevDevice *physdev;
+ char *physdev_path;
char *driver;
guint open_id;
guint32 open_tries;
+ guint full_id;
- MMSerialPort *probe_port;
+ MMAtSerialPort *probe_port;
+ MMQcdmSerialPort *qcdm_port;
guint32 probed_caps;
ProbeState probe_state;
guint probe_id;
@@ -112,7 +118,7 @@ typedef struct {
static MMPluginBaseSupportsTask *
supports_task_new (MMPluginBase *self,
GUdevDevice *port,
- GUdevDevice *physdev,
+ const char *physdev_path,
const char *driver,
MMSupportsPortResultFunc callback,
gpointer callback_data)
@@ -123,7 +129,7 @@ supports_task_new (MMPluginBase *self,
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL);
g_return_val_if_fail (port != NULL, NULL);
- g_return_val_if_fail (physdev != NULL, NULL);
+ g_return_val_if_fail (physdev_path != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (callback != NULL, NULL);
@@ -132,7 +138,7 @@ supports_task_new (MMPluginBase *self,
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
priv->plugin = self;
priv->port = g_object_ref (port);
- priv->physdev = g_object_ref (physdev);
+ priv->physdev_path = g_strdup (physdev_path);
priv->driver = g_strdup (driver);
priv->callback = callback;
priv->callback_data = callback_data;
@@ -158,13 +164,13 @@ mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task)
return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->port;
}
-GUdevDevice *
-mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task)
+const char *
+mm_plugin_base_supports_task_get_physdev_path (MMPluginBaseSupportsTask *task)
{
g_return_val_if_fail (task != NULL, NULL);
g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL);
- return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->physdev;
+ return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->physdev_path;
}
const char *
@@ -198,6 +204,11 @@ mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task,
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
g_return_if_fail (priv->callback != NULL);
+ if (priv->full_id) {
+ g_source_remove (priv->full_id);
+ priv->full_id = 0;
+ }
+
subsys = g_udev_device_get_subsystem (priv->port);
name = g_udev_device_get_name (priv->port);
@@ -239,11 +250,11 @@ supports_task_dispose (GObject *object)
{
MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object);
- if (MM_IS_SERIAL_PORT (priv->port))
- mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->port));
+ if (MM_IS_SERIAL_PORT (priv->probe_port))
+ mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->probe_port));
g_object_unref (priv->port);
- g_object_unref (priv->physdev);
+ g_free (priv->physdev_path);
g_free (priv->driver);
g_free (priv->probe_resp);
g_clear_error (&(priv->probe_error));
@@ -251,11 +262,19 @@ supports_task_dispose (GObject *object)
if (priv->open_id)
g_source_remove (priv->open_id);
+ if (priv->full_id)
+ g_source_remove (priv->full_id);
if (priv->probe_id)
g_source_remove (priv->probe_id);
- if (priv->probe_port)
+ if (priv->probe_port) {
+ mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
g_object_unref (priv->probe_port);
+ }
+ if (priv->qcdm_port) {
+ mm_serial_port_close (MM_SERIAL_PORT (priv->qcdm_port));
+ g_object_unref (priv->qcdm_port);
+ }
G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object);
}
@@ -334,7 +353,8 @@ parse_cpin (const char *buf)
|| strcasestr (buf, "PH-SP PIN")
|| strcasestr (buf, "PH-SP PUK")
|| strcasestr (buf, "PH-CORP PIN")
- || strcasestr (buf, "PH-CORP PUK"))
+ || strcasestr (buf, "PH-CORP PUK")
+ || strcasestr (buf, "READY"))
return MM_PLUGIN_BASE_PORT_CAP_GSM;
return 0;
@@ -349,6 +369,49 @@ parse_cgmm (const char *buf)
return 0;
}
+static const char *dq_strings[] = {
+ /* Option Icera-based devices */
+ "option/faema_",
+ "os_logids.h",
+ /* Sierra CnS port */
+ "NETWORK SERVICE CHANGE",
+ NULL
+};
+
+static void
+port_buffer_full (MMSerialPort *port, GByteArray *buffer, gpointer user_data)
+{
+ MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (user_data);
+ const char **iter;
+ size_t iter_len;
+ int i;
+
+ /* Check for an immediate disqualification response. There are some
+ * ports (Option Icera-based chipsets have them, as do Qualcomm Gobi
+ * devices before their firmware is loaded) that just shouldn't be
+ * probed if we get a certain response because we know they can't be
+ * used. Kernel bugs (at least with 2.6.31 and 2.6.32) also trigger port
+ * flow control kernel oopses if we read too much data for these ports.
+ */
+
+ for (iter = &dq_strings[0]; iter && *iter; iter++) {
+ /* Search in the response for the item; the response could have embedded
+ * nulls so we can't use memcmp() or strstr() on the whole response.
+ */
+ iter_len = strlen (*iter);
+ for (i = 0; i < buffer->len - iter_len; i++) {
+ if (!memcmp (&buffer->data[i], *iter, iter_len)) {
+ /* Immediately close the port and complete probing */
+ priv->probed_caps = 0;
+ mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
+ probe_complete (task);
+ return;
+ }
+ }
+ }
+}
+
static gboolean
emit_probe_result (gpointer user_data)
{
@@ -356,9 +419,15 @@ emit_probe_result (gpointer user_data)
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
MMPlugin *self = mm_plugin_base_supports_task_get_plugin (task);
- /* Close the serial port */
- g_object_unref (task_priv->probe_port);
- task_priv->probe_port = NULL;
+ /* Close the serial ports */
+ if (task_priv->probe_port) {
+ g_object_unref (task_priv->probe_port);
+ task_priv->probe_port = NULL;
+ }
+ if (task_priv->qcdm_port) {
+ g_object_unref (task_priv->qcdm_port);
+ task_priv->qcdm_port = NULL;
+ }
task_priv->probe_id = 0;
g_signal_emit (self, signals[PROBE_RESULT], 0, task, task_priv->probed_caps);
@@ -368,17 +437,122 @@ emit_probe_result (gpointer user_data)
static void
probe_complete (MMPluginBaseSupportsTask *task)
{
- MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ MMPort *port;
+ port = priv->probe_port ? MM_PORT (priv->probe_port) : MM_PORT (priv->qcdm_port);
+ g_assert (port);
g_hash_table_insert (cached_caps,
- g_strdup (mm_port_get_device (MM_PORT (task_priv->probe_port))),
- GUINT_TO_POINTER (task_priv->probed_caps));
+ g_strdup (mm_port_get_device (port)),
+ GUINT_TO_POINTER (priv->probed_caps));
+
+ priv->probe_id = g_idle_add (emit_probe_result, task);
+}
+
+static void
+qcdm_verinfo_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMPluginBaseSupportsTask *task;
+ MMPluginBaseSupportsTaskPrivate *priv;
+ QCDMResult *result;
+ GError *dm_error = NULL;
+
+ /* Just the initial poke; ignore it */
+ if (!user_data)
+ return;
+
+ task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
+ priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+
+ if (error) {
+ /* Probably not a QCDM port */
+ goto done;
+ }
+
+ /* Parse the response */
+ result = qcdm_cmd_version_info_result ((const char *) response->data, response->len, &dm_error);
+ if (!result) {
+ g_warning ("(%s) failed to parse QCDM version info command result: (%d) %s.",
+ g_udev_device_get_name (priv->port),
+ dm_error ? dm_error->code : -1,
+ dm_error && dm_error->message ? dm_error->message : "(unknown)");
+ g_clear_error (&dm_error);
+ goto done;
+ }
+
+ /* yay, probably a QCDM port */
+ qcdm_result_unref (result);
+ priv->probed_caps |= MM_PLUGIN_BASE_PORT_CAP_QCDM;
- task_priv->probe_id = g_idle_add (emit_probe_result, task);
+done:
+ probe_complete (task);
}
static void
-parse_response (MMSerialPort *port,
+try_qcdm_probe (MMPluginBaseSupportsTask *task)
+{
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ const char *name;
+ GError *error = NULL;
+ GByteArray *verinfo = NULL, *verinfo2;
+ gint len;
+
+ /* Close the AT port */
+ if (priv->probe_port) {
+ mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
+ g_object_unref (priv->probe_port);
+ priv->probe_port = NULL;
+ }
+
+ /* Open the QCDM port */
+ name = g_udev_device_get_name (priv->port);
+ g_assert (name);
+ priv->qcdm_port = mm_qcdm_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ if (priv->qcdm_port == NULL) {
+ g_warning ("(%s) failed to create new QCDM serial port.", name);
+ probe_complete (task);
+ return;
+ }
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->qcdm_port), &error)) {
+ g_warning ("(%s) failed to open new QCDM serial port: (%d) %s.",
+ name,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ probe_complete (task);
+ return;
+ }
+
+ /* Build up the probe command */
+ verinfo = g_byte_array_sized_new (50);
+ len = qcdm_cmd_version_info_new ((char *) verinfo->data, 50, &error);
+ if (len <= 0) {
+ g_byte_array_free (verinfo, TRUE);
+ g_warning ("(%s) failed to create QCDM version info command: (%d) %s.",
+ name,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ probe_complete (task);
+ return;
+ }
+ verinfo->len = len;
+
+ /* Queuing the command takes ownership over it; copy it for the second try */
+ verinfo2 = g_byte_array_sized_new (verinfo->len);
+ g_byte_array_append (verinfo2, verinfo->data, verinfo->len);
+
+ /* Send the command twice; the ports often need to be woken up */
+ mm_qcdm_serial_port_queue_command (priv->qcdm_port, verinfo, 3, qcdm_verinfo_cb, NULL);
+ mm_qcdm_serial_port_queue_command (priv->qcdm_port, verinfo2, 3, qcdm_verinfo_cb, task);
+}
+
+static void
+parse_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data);
@@ -391,7 +565,7 @@ real_handle_probe_response (MMPluginBase *self,
const GError *error)
{
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
- MMSerialPort *port = task_priv->probe_port;
+ MMAtSerialPort *port = task_priv->probe_port;
gboolean ignore_error = FALSE;
/* Some modems (Huawei E160g) won't respond to +GCAP with no SIM, but
@@ -401,16 +575,16 @@ real_handle_probe_response (MMPluginBase *self,
ignore_error = TRUE;
if (error && !ignore_error) {
- if (error->code == MM_SERIAL_RESPONSE_TIMEOUT) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
/* Try GCAP again */
if (task_priv->probe_state < PROBE_STATE_GCAP_TRY3) {
task_priv->probe_state++;
- mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
} else {
/* Otherwise, if all the GCAP tries timed out, ignore the port
- * as it's probably not an AT-capable port.
+ * as it's probably not an AT-capable port. Try QCDM.
*/
- probe_complete (task);
+ try_qcdm_probe (task);
}
return;
}
@@ -459,19 +633,19 @@ real_handle_probe_response (MMPluginBase *self,
switch (task_priv->probe_state) {
case PROBE_STATE_GCAP_TRY2:
case PROBE_STATE_GCAP_TRY3:
- mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
break;
case PROBE_STATE_ATI:
/* After the last GCAP attempt, try ATI */
- mm_serial_port_queue_command (port, "I", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "I", 3, parse_response, task);
break;
case PROBE_STATE_CPIN:
/* After the ATI attempt, try CPIN */
- mm_serial_port_queue_command (port, "+CPIN?", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+CPIN?", 3, parse_response, task);
break;
case PROBE_STATE_CGMM:
/* After the CPIN attempt, try CGMM */
- mm_serial_port_queue_command (port, "+CGMM", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+CGMM", 3, parse_response, task);
break;
default:
/* Probably not GSM or CDMA */
@@ -515,7 +689,7 @@ handle_probe_response (gpointer user_data)
}
static void
-parse_response (MMSerialPort *port,
+parse_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -545,7 +719,7 @@ parse_response (MMSerialPort *port,
static void flash_done (MMSerialPort *port, GError *error, gpointer user_data);
static void
-custom_init_response (MMSerialPort *port,
+custom_init_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -557,11 +731,11 @@ custom_init_response (MMSerialPort *port,
task_priv->custom_init_tries++;
if (task_priv->custom_init_tries < task_priv->custom_init_max_tries) {
/* Try the custom command again */
- flash_done (port, NULL, user_data);
+ flash_done (MM_SERIAL_PORT (port), NULL, user_data);
return;
} else if (task_priv->custom_init_fail_if_timeout) {
/* Fail the probe if the plugin wanted it and the command timed out */
- if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
probe_complete (task);
return;
}
@@ -569,7 +743,7 @@ custom_init_response (MMSerialPort *port,
}
/* Otherwise proceed to probing */
- mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data);
+ mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data);
}
static void
@@ -583,14 +757,14 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data)
if (task_priv->custom_init) {
if (!delay_secs)
delay_secs = 3;
- mm_serial_port_queue_command (port,
- task_priv->custom_init,
- delay_secs,
- custom_init_response,
- user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port),
+ task_priv->custom_init,
+ delay_secs,
+ custom_init_response,
+ user_data);
} else {
/* Otherwise start normal probing */
- custom_init_response (port, NULL, NULL, user_data);
+ custom_init_response (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data);
}
}
@@ -603,7 +777,7 @@ try_open (gpointer user_data)
task_priv->open_id = 0;
- if (!mm_serial_port_open (task_priv->probe_port, &error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (task_priv->probe_port), &error)) {
if (++task_priv->open_tries > 4) {
/* took too long to open the port; give up */
g_warning ("(%s): failed to open after 4 tries.",
@@ -611,7 +785,7 @@ try_open (gpointer user_data)
probe_complete (task);
} else if (g_error_matches (error,
MM_SERIAL_ERROR,
- MM_SERIAL_OPEN_FAILED_NO_DEVICE)) {
+ MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) {
/* this is nozomi being dumb; try again */
task_priv->open_id = g_timeout_add_seconds (1, try_open, task);
} else {
@@ -626,10 +800,13 @@ try_open (gpointer user_data)
port = mm_plugin_base_supports_task_get_port (task);
g_assert (port);
+ task_priv->full_id = g_signal_connect (task_priv->probe_port, "buffer-full",
+ G_CALLBACK (port_buffer_full), task);
+
g_debug ("(%s): probe requested by plugin '%s'",
g_udev_device_get_name (port),
mm_plugin_get_name (MM_PLUGIN (task_priv->plugin)));
- mm_serial_port_flash (task_priv->probe_port, 100, flash_done, task);
+ mm_serial_port_flash (MM_SERIAL_PORT (task_priv->probe_port), 100, TRUE, flash_done, task);
}
return FALSE;
@@ -641,7 +818,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
GError **error)
{
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
- MMSerialPort *serial;
+ MMAtSerialPort *serial;
const char *name;
GUdevDevice *port;
@@ -654,7 +831,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
name = g_udev_device_get_name (port);
g_assert (name);
- serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
if (serial == NULL) {
g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Failed to create new serial port.");
@@ -666,10 +843,10 @@ mm_plugin_base_probe_port (MMPluginBase *self,
MM_PORT_CARRIER_DETECT, FALSE,
NULL);
- mm_serial_port_set_response_parser (serial,
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ mm_at_serial_port_set_response_parser (serial,
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
/* Open the port */
task_priv->probe_port = serial;
@@ -695,20 +872,6 @@ mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self,
static void
modem_destroyed (gpointer data, GObject *modem)
{
- MMPluginBase *self = MM_PLUGIN_BASE (data);
- MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
- GHashTableIter iter;
- gpointer key, value;
-
- /* Remove it from the modems info */
- g_hash_table_iter_init (&iter, priv->modems);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- if (value == modem) {
- g_hash_table_iter_remove (&iter);
- break;
- }
- }
-
/* Since we don't track port cached capabilities on a per-modem basis,
* we just have to live with blowing away the cached capabilities whenever
* a modem gets removed. Could do better here by storing a structure
@@ -719,48 +882,6 @@ modem_destroyed (gpointer data, GObject *modem)
g_hash_table_remove_all (cached_caps);
}
-MMModem *
-mm_plugin_base_find_modem (MMPluginBase *self,
- const char *master_device)
-{
- MMPluginBasePrivate *priv;
-
- g_return_val_if_fail (self != NULL, NULL);
- g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL);
- g_return_val_if_fail (master_device != NULL, NULL);
- g_return_val_if_fail (strlen (master_device) > 0, NULL);
-
- priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
- return g_hash_table_lookup (priv->modems, master_device);
-}
-
-/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
-
-static int hex2num (char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- return -1;
-}
-
-static int hex2byte (const char *hex)
-{
- int a, b;
- a = hex2num(*hex++);
- if (a < 0)
- return -1;
- b = hex2num(*hex++);
- if (b < 0)
- return -1;
- return (a << 4) | b;
-}
-
-/* End from hostap */
-
gboolean
mm_plugin_base_get_device_ids (MMPluginBase *self,
const char *subsys,
@@ -793,8 +914,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self,
goto out;
if (vendor) {
- *vendor = (guint16) (hex2byte (vid + 2) & 0xFF);
- *vendor |= (guint16) ((hex2byte (vid) & 0xFF) << 8);
+ *vendor = (guint16) (utils_hex2byte (vid + 2) & 0xFF);
+ *vendor |= (guint16) ((utils_hex2byte (vid) & 0xFF) << 8);
}
pid = g_udev_device_get_property (device, "ID_MODEL_ID");
@@ -804,8 +925,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self,
}
if (product) {
- *product = (guint16) (hex2byte (pid + 2) & 0xFF);
- *product |= (guint16) ((hex2byte (pid) & 0xFF) << 8);
+ *product = (guint16) (utils_hex2byte (pid + 2) & 0xFF);
+ *product |= (guint16) ((utils_hex2byte (pid) & 0xFF) << 8);
}
success = TRUE;
@@ -859,58 +980,21 @@ get_driver_name (GUdevDevice *device)
return ret;
}
-static GUdevDevice *
-real_find_physical_device (MMPluginBase *plugin, GUdevDevice *child)
-{
- GUdevDevice *iter, *old = NULL;
- GUdevDevice *physdev = NULL;
- const char *subsys, *type;
- guint32 i = 0;
- gboolean is_usb = FALSE, is_pci = FALSE;
-
- g_return_val_if_fail (child != NULL, NULL);
-
- iter = g_object_ref (child);
- while (iter && i++ < 8) {
- subsys = g_udev_device_get_subsystem (iter);
- if (subsys) {
- if (is_usb || !strcmp (subsys, "usb")) {
- is_usb = TRUE;
- type = g_udev_device_get_devtype (iter);
- if (type && !strcmp (type, "usb_device")) {
- physdev = iter;
- break;
- }
- } else if (is_pci || !strcmp (subsys, "pci")) {
- is_pci = TRUE;
- physdev = iter;
- break;
- }
- }
-
- old = iter;
- iter = g_udev_device_get_parent (old);
- g_object_unref (old);
- }
-
- return physdev;
-}
-
static MMPluginSupportsResult
supports_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer callback_data)
{
MMPluginBase *self = MM_PLUGIN_BASE (plugin);
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
char *driver = NULL, *key = NULL;
MMPluginBaseSupportsTask *task;
MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- MMModem *existing;
- const char *master_path;
key = get_key (subsys, name);
task = g_hash_table_lookup (priv->tasks, key);
@@ -923,22 +1007,14 @@ supports_port (MMPlugin *plugin,
if (!port)
goto out;
- physdev = MM_PLUGIN_BASE_GET_CLASS (self)->find_physical_device (self, port);
- if (!physdev)
- goto out;
-
driver = get_driver_name (port);
if (!driver)
goto out;
- task = supports_task_new (self, port, physdev, driver, callback, callback_data);
+ task = supports_task_new (self, port, physdev_path, driver, callback, callback_data);
g_assert (task);
g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task));
- /* Help the plugin out a bit by finding an existing modem for this port */
- master_path = g_udev_device_get_sysfs_path (physdev);
- existing = g_hash_table_lookup (priv->modems, master_path);
-
result = MM_PLUGIN_BASE_GET_CLASS (self)->supports_port (self, existing, task);
if (result != MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS) {
/* If the plugin doesn't support the port at all, the supports task is
@@ -949,8 +1025,6 @@ supports_port (MMPlugin *plugin,
g_object_unref (task);
out:
- if (physdev)
- g_object_unref (physdev);
if (port)
g_object_unref (port);
g_free (key);
@@ -984,14 +1058,14 @@ static MMModem *
grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error)
{
MMPluginBase *self = MM_PLUGIN_BASE (plugin);
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
MMPluginBaseSupportsTask *task;
+ MMModem *modem = NULL;
char *key;
- MMModem *existing = NULL, *modem = NULL;
- const char *master_path;
key = get_key (subsys, name);
task = g_hash_table_lookup (priv->tasks, key);
@@ -1000,16 +1074,10 @@ grab_port (MMPlugin *plugin,
g_return_val_if_fail (task != NULL, FALSE);
}
- /* Help the plugin out a bit by finding an existing modem for this port */
- master_path = g_udev_device_get_sysfs_path (mm_plugin_base_supports_task_get_physdev (task));
- existing = g_hash_table_lookup (priv->modems, master_path);
-
/* Let the modem grab the port */
modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self, existing, task, error);
- if (modem && !existing) {
- g_hash_table_insert (priv->modems, g_strdup (master_path), modem);
+ if (modem && !existing)
g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self);
- }
g_hash_table_remove (priv->tasks, key);
g_free (key);
@@ -1039,7 +1107,6 @@ mm_plugin_base_init (MMPluginBase *self)
priv->client = g_udev_client_new (subsys);
- priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_object_unref);
}
@@ -1086,7 +1153,6 @@ finalize (GObject *object)
g_object_unref (priv->client);
- g_hash_table_destroy (priv->modems);
g_hash_table_destroy (priv->tasks);
G_OBJECT_CLASS (mm_plugin_base_parent_class)->finalize (object);
@@ -1099,7 +1165,6 @@ mm_plugin_base_class_init (MMPluginBaseClass *klass)
g_type_class_add_private (object_class, sizeof (MMPluginBasePrivate));
- klass->find_physical_device = real_find_physical_device;
klass->handle_probe_response = real_handle_probe_response;
/* Virtual methods */
diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h
index 49e68d4..a32d53b 100644
--- a/src/mm-plugin-base.h
+++ b/src/mm-plugin-base.h
@@ -37,6 +37,7 @@
#define MM_PLUGIN_BASE_PORT_CAP_W 0x0080 /* Wireless commands */
#define MM_PLUGIN_BASE_PORT_CAP_IS856 0x0100 /* CDMA 3G EVDO rev 0 */
#define MM_PLUGIN_BASE_PORT_CAP_IS856_A 0x0200 /* CDMA 3G EVDO rev A */
+#define MM_PLUGIN_BASE_PORT_CAP_QCDM 0x0400 /* QCDM-capable port */
#define MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK (mm_plugin_base_supports_task_get_type ())
#define MM_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTask))
@@ -59,7 +60,7 @@ MMPlugin *mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *tas
GUdevDevice *mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task);
-GUdevDevice *mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task);
+const char *mm_plugin_base_supports_task_get_physdev_path (MMPluginBaseSupportsTask *task);
const char *mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *task);
@@ -107,13 +108,6 @@ struct _MMPluginBaseClass {
void (*cancel_task) (MMPluginBase *plugin,
MMPluginBaseSupportsTask *task);
- /* Find a the physical device of a port, ie the USB or PCI or whatever
- * "master" device that owns the port. The GUdevDevice object returned
- * will be unref-ed by the caller.
- */
- GUdevDevice * (*find_physical_device) (MMPluginBase *plugin,
- GUdevDevice *port);
-
void (*handle_probe_response) (MMPluginBase *plugin,
MMPluginBaseSupportsTask *task,
const char *command,
@@ -128,9 +122,6 @@ struct _MMPluginBaseClass {
GType mm_plugin_base_get_type (void);
-MMModem *mm_plugin_base_find_modem (MMPluginBase *self,
- const char *master_device);
-
gboolean mm_plugin_base_get_device_ids (MMPluginBase *self,
const char *subsys,
const char *name,
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 830f2d6..7e5c582 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -28,15 +28,24 @@ MMPluginSupportsResult
mm_plugin_supports_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer user_data)
{
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
g_return_val_if_fail (subsys != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (physdev_path != NULL, FALSE);
g_return_val_if_fail (callback != NULL, FALSE);
- return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name, callback, user_data);
+ return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin,
+ subsys,
+ name,
+ physdev_path,
+ existing,
+ callback,
+ user_data);
}
void
@@ -55,13 +64,14 @@ MMModem *
mm_plugin_grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error)
{
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
g_return_val_if_fail (subsys != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
- return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, error);
+ return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, existing, error);
}
/*****************************************************************************/
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
index d1e85b6..4f1dfc0 100644
--- a/src/mm-plugin.h
+++ b/src/mm-plugin.h
@@ -70,6 +70,8 @@ struct _MMPlugin {
MMPluginSupportsResult (*supports_port) (MMPlugin *self,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer user_data);
@@ -94,6 +96,7 @@ struct _MMPlugin {
MMModem * (*grab_port) (MMPlugin *self,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error);
};
@@ -104,6 +107,8 @@ const char *mm_plugin_get_name (MMPlugin *plugin);
MMPluginSupportsResult mm_plugin_supports_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer user_data);
@@ -114,6 +119,7 @@ void mm_plugin_cancel_supports_port (MMPlugin *plugin,
MMModem *mm_plugin_grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error);
#endif /* MM_PLUGIN_H */
diff --git a/src/mm-port.c b/src/mm-port.c
index 7e8edc4..b2018fe 100644
--- a/src/mm-port.c
+++ b/src/mm-port.c
@@ -19,6 +19,7 @@
#include <string.h>
#include "mm-port.h"
+#include "mm-options.h"
G_DEFINE_TYPE (MMPort, mm_port, G_TYPE_OBJECT)
@@ -45,6 +46,26 @@ typedef struct {
/*****************************************************************************/
+const char *
+mm_port_type_to_name (MMPortType ptype)
+{
+ switch (ptype) {
+ case MM_PORT_TYPE_PRIMARY:
+ return "primary";
+ case MM_PORT_TYPE_SECONDARY:
+ return "secondary";
+ case MM_PORT_TYPE_IGNORED:
+ return "ignored";
+ case MM_PORT_TYPE_QCDM:
+ return "QCDM";
+ default:
+ break;
+ }
+ return "(unknown)";
+}
+
+/*****************************************************************************/
+
static GObject*
constructor (GType type,
guint n_construct_params,
@@ -140,6 +161,16 @@ mm_port_set_connected (MMPort *self, gboolean connected)
if (priv->connected != connected) {
priv->connected = connected;
g_object_notify (G_OBJECT (self), MM_PORT_CONNECTED);
+ if (mm_options_debug()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): port now %s",
+ tv.tv_sec,
+ tv.tv_usec,
+ priv->device,
+ connected ? "connected" : "disconnected");
+ }
}
}
diff --git a/src/mm-port.h b/src/mm-port.h
index b537618..43f78f4 100644
--- a/src/mm-port.h
+++ b/src/mm-port.h
@@ -33,8 +33,9 @@ typedef enum {
MM_PORT_TYPE_PRIMARY,
MM_PORT_TYPE_SECONDARY,
MM_PORT_TYPE_IGNORED,
+ MM_PORT_TYPE_QCDM,
- MM_PORT_TYPE_LAST = MM_PORT_TYPE_IGNORED
+ MM_PORT_TYPE_LAST = MM_PORT_TYPE_QCDM
} MMPortType;
#define MM_TYPE_PORT (mm_port_get_type ())
@@ -75,5 +76,7 @@ gboolean mm_port_get_connected (MMPort *self);
void mm_port_set_connected (MMPort *self, gboolean connected);
+const char * mm_port_type_to_name (MMPortType ptype);
+
#endif /* MM_PORT_H */
diff --git a/src/mm-qcdm-serial-port.c b/src/mm-qcdm-serial-port.c
new file mode 100644
index 0000000..1ce27e7
--- /dev/null
+++ b/src/mm-qcdm-serial-port.c
@@ -0,0 +1,225 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-qcdm-serial-port.h"
+#include "mm-errors.h"
+#include "mm-options.h"
+#include "libqcdm/src/com.h"
+#include "libqcdm/src/utils.h"
+
+G_DEFINE_TYPE (MMQcdmSerialPort, mm_qcdm_serial_port, MM_TYPE_SERIAL_PORT)
+
+#define MM_QCDM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPortPrivate))
+
+typedef struct {
+ gboolean foo;
+} MMQcdmSerialPortPrivate;
+
+
+/*****************************************************************************/
+
+static gboolean
+parse_response (MMSerialPort *port, GByteArray *response, GError **error)
+{
+ int i;
+
+ /* Look for the QCDM packet termination character; if we found it, treat
+ * the buffer as a qcdm command.
+ */
+ for (i = 0; i < response->len; i++) {
+ if (response->data[i] == 0x7E)
+ return TRUE;
+ }
+
+ /* Otherwise, need more data from the device */
+ return FALSE;
+}
+
+static gsize
+handle_response (MMSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data)
+{
+ MMQcdmSerialResponseFn response_callback = (MMQcdmSerialResponseFn) callback;
+ GByteArray *unescaped = NULL;
+ GError *dm_error = NULL;
+ gsize used = 0;
+
+ /* Ignore empty frames */
+ if (response->len > 0 && response->data[0] == 0x7E)
+ return 1;
+
+ if (!error) {
+ gboolean more = FALSE, success;
+ gsize unescaped_len = 0;
+
+ /* FIXME: don't munge around with byte array internals */
+ unescaped = g_byte_array_sized_new (1024);
+ success = dm_decapsulate_buffer ((const char *) response->data,
+ response->len,
+ (char *) unescaped->data,
+ 1024,
+ &unescaped_len,
+ &used,
+ &more);
+ if (!success) {
+ g_set_error_literal (&dm_error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to unescape QCDM packet.");
+ g_byte_array_free (unescaped, TRUE);
+ unescaped = NULL;
+ } else if (more) {
+ /* Need more data; we shouldn't have gotten here since the parse
+ * function checks for the end-of-frame marker, but whatever.
+ */
+ return 0;
+ } else {
+ /* Successfully decapsulated the DM command */
+ unescaped->len = (guint) unescaped_len;
+ }
+ }
+
+ response_callback (MM_QCDM_SERIAL_PORT (port),
+ unescaped,
+ dm_error ? dm_error : error,
+ callback_data);
+
+ if (unescaped)
+ g_byte_array_free (unescaped, TRUE);
+
+ return used;
+}
+
+/*****************************************************************************/
+
+void
+mm_qcdm_serial_port_queue_command (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_QCDM_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ /* 'command' is expected to be already CRC-ed and escaped */
+ mm_serial_port_queue_command (MM_SERIAL_PORT (self),
+ command,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+void
+mm_qcdm_serial_port_queue_command_cached (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_QCDM_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ /* 'command' is expected to be already CRC-ed and escaped */
+ mm_serial_port_queue_command_cached (MM_SERIAL_PORT (self),
+ command,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+static void
+debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len)
+{
+ static GString *debug = NULL;
+ const char *s = buf;
+ GTimeVal tv;
+
+ if (!debug)
+ debug = g_string_sized_new (512);
+
+ g_string_append (debug, prefix);
+
+ while (len--)
+ g_string_append_printf (debug, " %02x", (guint8) (*s++ & 0xFF));
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): %s",
+ tv.tv_sec,
+ tv.tv_usec,
+ mm_port_get_device (MM_PORT (port)),
+ debug->str);
+ g_string_truncate (debug, 0);
+}
+
+/*****************************************************************************/
+
+static gboolean
+config_fd (MMSerialPort *port, int fd, GError **error)
+{
+ return qcdm_port_setup (fd, error);
+}
+
+/*****************************************************************************/
+
+MMQcdmSerialPort *
+mm_qcdm_serial_port_new (const char *name, MMPortType ptype)
+{
+ return MM_QCDM_SERIAL_PORT (g_object_new (MM_TYPE_QCDM_SERIAL_PORT,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
+ MM_PORT_TYPE, ptype,
+ NULL));
+}
+
+static void
+mm_qcdm_serial_port_init (MMQcdmSerialPort *self)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ G_OBJECT_CLASS (mm_qcdm_serial_port_parent_class)->finalize (object);
+}
+
+static void
+mm_qcdm_serial_port_class_init (MMQcdmSerialPortClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMSerialPortClass *port_class = MM_SERIAL_PORT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMQcdmSerialPortPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ port_class->parse_response = parse_response;
+ port_class->handle_response = handle_response;
+ port_class->config_fd = config_fd;
+ port_class->debug_log = debug_log;
+}
diff --git a/src/mm-qcdm-serial-port.h b/src/mm-qcdm-serial-port.h
new file mode 100644
index 0000000..e70e124
--- /dev/null
+++ b/src/mm-qcdm-serial-port.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_QCDM_SERIAL_PORT_H
+#define MM_QCDM_SERIAL_PORT_H
+
+#include <glib.h>
+#include <glib/gtypes.h>
+#include <glib-object.h>
+
+#include "mm-serial-port.h"
+
+#define MM_TYPE_QCDM_SERIAL_PORT (mm_qcdm_serial_port_get_type ())
+#define MM_QCDM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPort))
+#define MM_QCDM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPortClass))
+#define MM_IS_QCDM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_QCDM_SERIAL_PORT))
+#define MM_IS_QCDM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_QCDM_SERIAL_PORT))
+#define MM_QCDM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPortClass))
+
+typedef struct _MMQcdmSerialPort MMQcdmSerialPort;
+typedef struct _MMQcdmSerialPortClass MMQcdmSerialPortClass;
+
+typedef void (*MMQcdmSerialResponseFn) (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data);
+
+struct _MMQcdmSerialPort {
+ MMSerialPort parent;
+};
+
+struct _MMQcdmSerialPortClass {
+ MMSerialPortClass parent;
+};
+
+GType mm_qcdm_serial_port_get_type (void);
+
+MMQcdmSerialPort *mm_qcdm_serial_port_new (const char *name, MMPortType ptype);
+
+void mm_qcdm_serial_port_queue_command (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data);
+
+void mm_qcdm_serial_port_queue_command_cached (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data);
+
+#endif /* MM_QCDM_SERIAL_PORT_H */
+
diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c
index 58985d9..7c9598e 100644
--- a/src/mm-serial-parsers.c
+++ b/src/mm-serial-parsers.c
@@ -190,7 +190,8 @@ mm_serial_parser_v0_destroy (gpointer data)
typedef struct {
GRegex *regex_ok;
GRegex *regex_connect;
- GRegex *regex_detailed_error;
+ GRegex *regex_cme_error;
+ GRegex *regex_cme_error_str;
GRegex *regex_unknown_error;
GRegex *regex_connect_failed;
} MMSerialParserV1;
@@ -205,7 +206,8 @@ mm_serial_parser_v1_new (void)
parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+$", flags, 0, NULL);
parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL);
- parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL);
+ parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL);
+ parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR: ([^\\n\\r]+)\\r\\n$", flags, 0, NULL);
parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL);
parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL);
@@ -219,9 +221,10 @@ mm_serial_parser_v1_parse (gpointer data,
{
MMSerialParserV1 *parser = (MMSerialParserV1 *) data;
GMatchInfo *match_info;
- GError *local_error;
- int code;
+ GError *local_error = NULL;
gboolean found = FALSE;
+ char *str;
+ int code;
g_return_val_if_fail (parser != NULL, FALSE);
g_return_val_if_fail (response != NULL, FALSE);
@@ -243,56 +246,70 @@ mm_serial_parser_v1_parse (gpointer data,
}
/* Now failures */
- code = MM_MOBILE_ERROR_UNKNOWN;
- local_error = NULL;
- found = g_regex_match_full (parser->regex_detailed_error,
+ /* Numeric CME errors */
+ found = g_regex_match_full (parser->regex_cme_error,
response->str, response->len,
0, 0, &match_info, NULL);
-
if (found) {
- char *str;
+ str = g_match_info_fetch (match_info, 1);
+ g_assert (str);
+ local_error = mm_mobile_error_for_code (atoi (str));
+ g_free (str);
+ g_match_info_free (match_info);
+ goto done;
+ }
+ /* String CME errors */
+ found = g_regex_match_full (parser->regex_cme_error_str,
+ response->str, response->len,
+ 0, 0, &match_info, NULL);
+ if (found) {
str = g_match_info_fetch (match_info, 1);
- if (str) {
- code = atoi (str);
- g_free (str);
- }
+ g_assert (str);
+ local_error = mm_mobile_error_for_string (str);
+ g_free (str);
g_match_info_free (match_info);
- } else
- found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, NULL, NULL);
+ goto done;
+ }
- if (found)
- local_error = mm_mobile_error_for_code (code);
- else {
- found = g_regex_match_full (parser->regex_connect_failed,
- response->str, response->len,
- 0, 0, &match_info, NULL);
- if (found) {
- char *str;
+ /* Last resort; unknown error */
+ found = g_regex_match_full (parser->regex_unknown_error,
+ response->str, response->len,
+ 0, 0, NULL, NULL);
+ if (found) {
+ local_error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
+ goto done;
+ }
- str = g_match_info_fetch (match_info, 1);
- if (str) {
- if (!strcmp (str, "NO CARRIER"))
- code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
- else if (!strcmp (str, "BUSY"))
- code = MM_MODEM_CONNECT_ERROR_BUSY;
- else if (!strcmp (str, "NO ANSWER"))
- code = MM_MODEM_CONNECT_ERROR_NO_ANSWER;
- else if (!strcmp (str, "NO DIALTONE"))
- code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE;
- else
- /* uhm... make something up (yes, ok, lie!). */
- code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
+ /* Connection failures */
+ found = g_regex_match_full (parser->regex_connect_failed,
+ response->str, response->len,
+ 0, 0, &match_info, NULL);
+ if (found) {
+ str = g_match_info_fetch (match_info, 1);
+ g_assert (str);
+
+ if (!strcmp (str, "NO CARRIER"))
+ code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
+ else if (!strcmp (str, "BUSY"))
+ code = MM_MODEM_CONNECT_ERROR_BUSY;
+ else if (!strcmp (str, "NO ANSWER"))
+ code = MM_MODEM_CONNECT_ERROR_NO_ANSWER;
+ else if (!strcmp (str, "NO DIALTONE"))
+ code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE;
+ else {
+ /* uhm... make something up (yes, ok, lie!). */
+ code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
+ }
- g_free (str);
- }
- g_match_info_free (match_info);
+ g_free (str);
+ g_match_info_free (match_info);
- local_error = mm_modem_connect_error_for_code (code);
- }
+ local_error = mm_modem_connect_error_for_code (code);
}
+done:
if (found)
response_clean (response);
@@ -313,7 +330,8 @@ mm_serial_parser_v1_destroy (gpointer data)
g_regex_unref (parser->regex_ok);
g_regex_unref (parser->regex_connect);
- g_regex_unref (parser->regex_detailed_error);
+ g_regex_unref (parser->regex_cme_error);
+ g_regex_unref (parser->regex_cme_error_str);
g_regex_unref (parser->regex_unknown_error);
g_regex_unref (parser->regex_connect_failed);
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
index 2600ae5..ed44167 100644
--- a/src/mm-serial-port.c
+++ b/src/mm-serial-port.c
@@ -11,14 +11,14 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#define _GNU_SOURCE /* for strcasestr() */
#include <stdio.h>
#include <stdlib.h>
-#include <termio.h>
+#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -51,20 +51,12 @@ enum {
#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate))
typedef struct {
+ guint32 open_count;
int fd;
GHashTable *reply_cache;
GIOChannel *channel;
GQueue *queue;
- GString *command;
- GString *response;
-
- gboolean connected;
-
- /* Response parser data */
- MMSerialResponseParserFn response_parser_fn;
- gpointer response_parser_user_data;
- GDestroyNotify response_parser_notify;
- GSList *unsolicited_msg_handlers;
+ GByteArray *response;
struct termios old_t;
@@ -148,12 +140,12 @@ void
mm_serial_port_print_config (MMSerialPort *port, const char *detail)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (port);
- struct termio stbuf;
+ struct termios stbuf;
int err;
- err = ioctl (priv->fd, TCGETA, &stbuf);
+ err = tcgetattr (priv->fd, &stbuf);
if (err) {
- g_warning ("*** %s (%s): (%s) TCGETA error %d",
+ g_warning ("*** %s (%s): (%s) tcgetattr() error %d",
__func__, detail, mm_port_get_device (MM_PORT (port)), errno);
return;
}
@@ -165,33 +157,6 @@ mm_serial_port_print_config (MMSerialPort *port, const char *detail)
}
#endif
-typedef struct {
- GRegex *regex;
- MMSerialUnsolicitedMsgFn callback;
- gpointer user_data;
- GDestroyNotify notify;
-} MMUnsolicitedMsgHandler;
-
-static void
-mm_serial_port_set_cached_reply (MMSerialPort *self,
- const char *command,
- const char *reply)
-{
- if (reply)
- g_hash_table_insert (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache,
- g_strdup (command),
- g_strdup (reply));
- else
- g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
-}
-
-static const char *
-mm_serial_port_get_cached_reply (MMSerialPort *self,
- const char *command)
-{
- return (char *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
-}
-
static int
parse_baudrate (guint i)
{
@@ -327,10 +292,10 @@ parse_stopbits (guint i)
}
static gboolean
-config_fd (MMSerialPort *self, GError **error)
+real_config_fd (MMSerialPort *self, int fd, GError **error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- struct termio stbuf;
+ struct termios stbuf;
int speed;
int bits;
int parity;
@@ -341,9 +306,9 @@ config_fd (MMSerialPort *self, GError **error)
parity = parse_parity (priv->parity);
stopbits = parse_stopbits (priv->stopbits);
- memset (&stbuf, 0, sizeof (struct termio));
- if (ioctl (priv->fd, TCGETA, &stbuf) != 0) {
- g_warning ("%s (%s): TCGETA error: %d",
+ memset (&stbuf, 0, sizeof (struct termios));
+ if (tcgetattr (fd, &stbuf) != 0) {
+ g_warning ("%s (%s): tcgetattr() error: %d",
__func__,
mm_port_get_device (MM_PORT (self)),
errno);
@@ -357,10 +322,16 @@ config_fd (MMSerialPort *self, GError **error)
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
- stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
+ /* Use software handshaking */
+ stbuf.c_iflag |= (IXON | IXOFF | IXANY);
- if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
+ /* Set up port speed and serial attributes; also ignore modem control
+ * lines since most drivers don't implement RTS/CTS anyway.
+ */
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS);
+ stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits | CLOCAL);
+
+ if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
g_set_error (error,
MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
@@ -373,105 +344,99 @@ config_fd (MMSerialPort *self, GError **error)
}
static void
-serial_debug (MMSerialPort *self, const char *prefix, const char *buf, int len)
+serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len)
{
- static GString *debug = NULL;
- const char *s;
-
- if (!mm_options_debug ())
- return;
-
- if (len < 0)
- len = strlen (buf);
-
- if (!debug)
- debug = g_string_sized_new (256);
-
- g_string_append (debug, prefix);
- g_string_append (debug, " '");
-
- s = buf;
- while (len--) {
- if (g_ascii_isprint (*s))
- g_string_append_c (debug, *s);
- else if (*s == '\r')
- g_string_append (debug, "<CR>");
- else if (*s == '\n')
- g_string_append (debug, "<LF>");
- else
- g_string_append_printf (debug, "\\%d", *s);
-
- s++;
- }
+ g_return_if_fail (len > 0);
- g_string_append_c (debug, '\'');
- g_debug ("(%s): %s", mm_port_get_device (MM_PORT (self)), debug->str);
- g_string_truncate (debug, 0);
+ if (mm_options_debug () && MM_SERIAL_PORT_GET_CLASS (self)->debug_log)
+ MM_SERIAL_PORT_GET_CLASS (self)->debug_log (self, prefix, buf, len);
}
static gboolean
mm_serial_port_send_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
GError **error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- const char *s;
- int status;
+ int status, i = 0;
int eagain_count = 1000;
+ const guint8 *p;
if (priv->fd < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"%s", "Sending command failed: device is not enabled");
return FALSE;
}
if (mm_port_get_connected (MM_PORT (self))) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"%s", "Sending command failed: device is connected");
return FALSE;
}
- g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2);
- g_string_append (priv->command, command);
-
- if (command[strlen (command)] != '\r')
- g_string_append_c (priv->command, '\r');
-
- serial_debug (self, "-->", priv->command->str, -1);
+ serial_debug (self, "-->", (const char *) command->data, command->len);
/* Only accept about 3 seconds of EAGAIN */
if (priv->send_delay > 0)
eagain_count = 3000000 / priv->send_delay;
- s = priv->command->str;
- while (*s) {
- status = write (priv->fd, s, 1);
+ while (i < command->len) {
+ p = &command->data[i];
+ status = write (priv->fd, p, 1);
if (status < 0) {
if (errno == EAGAIN) {
eagain_count--;
if (eagain_count <= 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"Sending command failed: '%s'", strerror (errno));
break;
}
} else {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"Sending command failed: '%s'", strerror (errno));
break;
}
} else
- s++;
+ i++;
if (priv->send_delay)
usleep (priv->send_delay);
}
- return *s == '\0';
+ return i == command->len;
+}
+
+static void
+mm_serial_port_set_cached_reply (MMSerialPort *self,
+ const GByteArray *command,
+ const GByteArray *response)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ if (response) {
+ GByteArray *cmd_copy = g_byte_array_sized_new (command->len);
+ GByteArray *rsp_copy = g_byte_array_sized_new (response->len);
+
+ g_byte_array_append (cmd_copy, command->data, command->len);
+ g_byte_array_append (rsp_copy, response->data, response->len);
+ g_hash_table_insert (priv->reply_cache, cmd_copy, rsp_copy);
+ } else
+ g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
+}
+
+static const GByteArray *
+mm_serial_port_get_cached_reply (MMSerialPort *self, GByteArray *command)
+{
+ return (const GByteArray *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
}
typedef struct {
- char *command;
- MMSerialResponseFn callback;
+ GByteArray *command;
+ GCallback callback;
gpointer user_data;
guint32 timeout;
gboolean cached;
@@ -500,11 +465,25 @@ mm_serial_port_schedule_queue_process (MMSerialPort *self)
g_source_unref (source);
}
+static gsize
+real_handle_response (MMSerialPort *self,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data)
+{
+ MMSerialResponseFn response_callback = (MMSerialResponseFn) callback;
+
+ response_callback (self, response, error, callback_data);
+ return response->len;
+}
+
static void
mm_serial_port_got_response (MMSerialPort *self, GError *error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
MMQueueData *info;
+ gsize consumed = priv->response->len;
if (priv->timeout_id) {
g_source_remove (priv->timeout_id);
@@ -514,19 +493,26 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error)
info = (MMQueueData *) g_queue_pop_head (priv->queue);
if (info) {
if (info->cached && !error)
- mm_serial_port_set_cached_reply (self, info->command, priv->response->str);
-
- if (info->callback)
- info->callback (self, priv->response, error, info->user_data);
+ mm_serial_port_set_cached_reply (self, info->command, priv->response);
+
+ if (info->callback) {
+ g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->handle_response != NULL);
+ consumed = MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
+ priv->response,
+ error,
+ info->callback,
+ info->user_data);
+ }
- g_free (info->command);
+ g_byte_array_free (info->command, TRUE);
g_slice_free (MMQueueData, info);
}
if (error)
g_error_free (error);
- g_string_truncate (priv->response, 0);
+ if (consumed)
+ g_byte_array_remove_range (priv->response, 0, consumed);
if (!g_queue_is_empty (priv->queue))
mm_serial_port_schedule_queue_process (self);
}
@@ -541,7 +527,7 @@ mm_serial_port_timed_out (gpointer data)
priv->timeout_id = 0;
error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_RESPONSE_TIMEOUT,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
"Serial command timed out");
/* FIXME: This is not completely correct - if the response finally arrives and there's
some other command waiting for response right now, the other command will
@@ -566,10 +552,10 @@ mm_serial_port_queue_process (gpointer data)
return FALSE;
if (info->cached) {
- const char *cached = mm_serial_port_get_cached_reply (self, info->command);
+ const GByteArray *cached = mm_serial_port_get_cached_reply (self, info->command);
if (cached) {
- g_string_append (priv->response, cached);
+ g_byte_array_append (priv->response, cached->data, cached->len);
mm_serial_port_got_response (self, NULL);
return FALSE;
}
@@ -590,111 +576,16 @@ mm_serial_port_queue_process (gpointer data)
return FALSE;
}
-void
-mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self,
- GRegex *regex,
- MMSerialUnsolicitedMsgFn callback,
- gpointer user_data,
- GDestroyNotify notify)
-{
- MMUnsolicitedMsgHandler *handler;
- MMSerialPortPrivate *priv;
-
- g_return_if_fail (MM_IS_SERIAL_PORT (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_PORT_GET_PRIVATE (self);
- priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
-}
-
-void
-mm_serial_port_set_response_parser (MMSerialPort *self,
- MMSerialResponseParserFn fn,
- gpointer user_data,
- GDestroyNotify notify)
-{
- MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
-
- g_return_if_fail (MM_IS_SERIAL_PORT (self));
-
- if (priv->response_parser_notify)
- priv->response_parser_notify (priv->response_parser_user_data);
-
- priv->response_parser_fn = fn;
- priv->response_parser_user_data = user_data;
- priv->response_parser_notify = notify;
-}
-
-static gboolean
-remove_eval_cb (const GMatchInfo *match_info,
- GString *result,
- gpointer user_data)
-{
- int *result_len = (int *) user_data;
- int start;
- int end;
-
- if (g_match_info_fetch_pos (match_info, 0, &start, &end))
- *result_len -= (end - start);
-
- return FALSE;
-}
-
-static void
-parse_unsolicited_messages (MMSerialPort *self,
- GString *response)
-{
- MMSerialPortPrivate *priv = MM_SERIAL_PORT_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 (MMSerialPort *self,
- GString *response,
+ GByteArray *response,
GError **error)
{
- MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
-
- g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
-
- parse_unsolicited_messages (self, response);
+ if (MM_SERIAL_PORT_GET_CLASS (self)->parse_unsolicited)
+ MM_SERIAL_PORT_GET_CLASS (self)->parse_unsolicited (self, response);
- return priv->response_parser_fn (priv->response_parser_user_data, response, error);
+ g_return_val_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->parse_response, FALSE);
+ return MM_SERIAL_PORT_GET_CLASS (self)->parse_response (self, response, error);
}
static gboolean
@@ -709,13 +600,15 @@ data_available (GIOChannel *source,
GIOStatus status;
if (condition & G_IO_HUP) {
- g_string_truncate (priv->response, 0);
- mm_serial_port_close (self);
+ if (priv->response->len)
+ g_byte_array_remove_range (priv->response, 0, priv->response->len);
+ mm_serial_port_close_force (self);
return FALSE;
}
if (condition & G_IO_ERR) {
- g_string_truncate (priv->response, 0);
+ if (priv->response->len)
+ g_byte_array_remove_range (priv->response, 0, priv->response->len);
return TRUE;
}
@@ -724,9 +617,13 @@ data_available (GIOChannel *source,
status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
if (status == G_IO_STATUS_ERROR) {
- g_warning ("%s", err->message);
- g_error_free (err);
- err = NULL;
+ if (err && err->message)
+ g_warning ("%s", err->message);
+ g_clear_error (&err);
+
+ /* Serial port is closed; we're done */
+ if (priv->watch_id == 0)
+ break;
}
/* If no bytes read, just let g_io_channel wait for more data */
@@ -735,14 +632,14 @@ data_available (GIOChannel *source,
if (bytes_read > 0) {
serial_debug (self, "<--", buf, bytes_read);
- g_string_append_len (priv->response, buf, bytes_read);
+ g_byte_array_append (priv->response, (const guint8 *) buf, bytes_read);
}
- /* Make sure the string doesn't grow too long */
- if (priv->response->len > SERIAL_BUF_SIZE) {
- g_warning ("%s (%s): response buffer filled before repsonse received",
- G_STRFUNC, mm_port_get_device (MM_PORT (self)));
- g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2));
+ /* Make sure the response doesn't grow too long */
+ if (priv->response->len > SERIAL_BUF_SIZE) {
+ /* Notify listeners and then trim the buffer */
+ g_signal_emit_by_name (self, "buffer-full", priv->response);
+ g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2));
}
if (parse_response (self, priv->response, &err))
@@ -792,13 +689,13 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- if (priv->fd >= 0) {
+ device = mm_port_get_device (MM_PORT (self));
+
+ if (priv->open_count) {
/* Already open */
- return TRUE;
+ goto success;
}
- device = mm_port_get_device (MM_PORT (self));
-
g_message ("(%s) opening serial device...", device);
devfile = g_strdup_printf ("/dev/%s", device);
errno = 0;
@@ -812,32 +709,29 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
*/
g_set_error (error,
MM_SERIAL_ERROR,
- (errno == ENODEV) ? MM_SERIAL_OPEN_FAILED_NO_DEVICE : MM_SERIAL_OPEN_FAILED,
+ (errno == ENODEV) ? MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE : MM_SERIAL_ERROR_OPEN_FAILED,
"Could not open serial device %s: %s", device, strerror (errno));
return FALSE;
}
if (ioctl (priv->fd, TIOCEXCL) < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
"Could not lock serial device %s: %s", device, strerror (errno));
- close (priv->fd);
- priv->fd = -1;
- return FALSE;
+ goto error;
}
- if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ /* Flush any waiting IO */
+ tcflush (priv->fd, TCIOFLUSH);
+
+ if (tcgetattr (priv->fd, &priv->old_t) < 0) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
"Could not open serial device %s: %s", device, strerror (errno));
- close (priv->fd);
- priv->fd = -1;
- return FALSE;
+ goto error;
}
- if (!config_fd (self, error)) {
- close (priv->fd);
- priv->fd = -1;
- return FALSE;
- }
+ g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->config_fd);
+ if (!MM_SERIAL_PORT_GET_CLASS (self)->config_fd (self, priv->fd, error))
+ goto error;
priv->channel = g_io_channel_unix_new (priv->fd);
g_io_channel_set_encoding (priv->channel, NULL, NULL);
@@ -849,17 +743,58 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
priv->connected_id = g_signal_connect (self, "notify::" MM_PORT_CONNECTED,
G_CALLBACK (port_connected), NULL);
+success:
+ priv->open_count++;
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s) device open count is %d (open)",
+ tv.tv_sec, tv.tv_usec, device, priv->open_count);
+ }
return TRUE;
+
+error:
+ close (priv->fd);
+ priv->fd = -1;
+ return FALSE;
+}
+
+gboolean
+mm_serial_port_is_open (MMSerialPort *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
+
+ return !!MM_SERIAL_PORT_GET_PRIVATE (self)->open_count;
}
void
mm_serial_port_close (MMSerialPort *self)
{
MMSerialPortPrivate *priv;
+ const char *device;
+ int i;
g_return_if_fail (MM_IS_SERIAL_PORT (self));
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ g_return_if_fail (priv->open_count > 0);
+
+ device = mm_port_get_device (MM_PORT (self));
+
+ priv->open_count--;
+
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s) device open count is %d (close)",
+ tv.tv_sec, tv.tv_usec, device, priv->open_count);
+ }
+
+ if (priv->open_count > 0)
+ return;
if (priv->connected_id) {
g_signal_handler_disconnect (self, priv->connected_id);
@@ -867,31 +802,74 @@ mm_serial_port_close (MMSerialPort *self)
}
if (priv->fd >= 0) {
- g_message ("(%s) closing serial device...", mm_port_get_device (MM_PORT (self)));
+ g_message ("(%s) closing serial device...", device);
mm_port_set_connected (MM_PORT (self), FALSE);
if (priv->channel) {
g_source_remove (priv->watch_id);
+ priv->watch_id = 0;
g_io_channel_shutdown (priv->channel, TRUE, NULL);
g_io_channel_unref (priv->channel);
priv->channel = NULL;
}
- if (priv->flash_id > 0) {
- g_source_remove (priv->flash_id);
- priv->flash_id = 0;
- }
+ mm_serial_port_flash_cancel (self);
- ioctl (priv->fd, TCSETA, &priv->old_t);
+ tcsetattr (priv->fd, TCSANOW, &priv->old_t);
close (priv->fd);
priv->fd = -1;
}
+
+ /* Clear the command queue */
+ for (i = 0; i < g_queue_get_length (priv->queue); i++) {
+ MMQueueData *item = g_queue_peek_nth (priv->queue, i);
+
+ if (item->callback) {
+ GError *error;
+ GByteArray *response;
+
+ g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->handle_response != NULL);
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_SEND_FAILED,
+ "Serial port is now closed");
+ response = g_byte_array_sized_new (1);
+ g_byte_array_append (response, (const guint8 *) "\0", 1);
+ MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
+ response,
+ error,
+ item->callback,
+ item->user_data);
+ g_error_free (error);
+ g_byte_array_free (response, TRUE);
+ }
+
+ g_byte_array_free (item->command, TRUE);
+ g_slice_free (MMQueueData, item);
+ }
+ g_queue_clear (priv->queue);
+}
+
+void
+mm_serial_port_close_force (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ g_return_if_fail (priv->open_count > 0);
+
+ /* Force the port to close */
+ priv->open_count = 1;
+ mm_serial_port_close (self);
}
static void
internal_queue_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
gboolean cached,
guint32 timeout_seconds,
MMSerialResponseFn callback,
@@ -904,15 +882,20 @@ internal_queue_command (MMSerialPort *self,
g_return_if_fail (command != NULL);
info = g_slice_new0 (MMQueueData);
- info->command = g_strdup (command);
+ if (take_command)
+ info->command = command;
+ else {
+ info->command = g_byte_array_sized_new (command->len);
+ g_byte_array_append (info->command, command->data, command->len);
+ }
info->cached = cached;
info->timeout = timeout_seconds;
- info->callback = callback;
+ info->callback = (GCallback) callback;
info->user_data = user_data;
/* Clear the cached value for this command if not asking for cached value */
if (!cached)
- mm_serial_port_set_cached_reply (self, command, NULL);
+ mm_serial_port_set_cached_reply (self, info->command, NULL);
g_queue_push_tail (priv->queue, info);
@@ -922,22 +905,24 @@ internal_queue_command (MMSerialPort *self,
void
mm_serial_port_queue_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data)
{
- internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data);
+ internal_queue_command (self, command, take_command, FALSE, timeout_seconds, callback, user_data);
}
void
mm_serial_port_queue_command_cached (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data)
{
- internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data);
+ internal_queue_command (self, command, take_command, TRUE, timeout_seconds, callback, user_data);
}
typedef struct {
@@ -1030,8 +1015,14 @@ flash_do (gpointer data)
priv->flash_id = 0;
- if (!set_speed (info->port, info->current_speed, &error))
- g_assert (error);
+ if (info->current_speed) {
+ if (!set_speed (info->port, info->current_speed, &error))
+ g_assert (error);
+ } else {
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_FLASH_FAILED,
+ "Failed to retrieve current speed");
+ }
info->callback (info->port, error, info->user_data);
g_clear_error (&error);
@@ -1042,6 +1033,7 @@ flash_do (gpointer data)
gboolean
mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time,
+ gboolean ignore_errors,
MMSerialFlashFn callback,
gpointer user_data)
{
@@ -1049,12 +1041,22 @@ mm_serial_port_flash (MMSerialPort *self,
MMSerialPortPrivate *priv;
speed_t cur_speed = 0;
GError *error = NULL;
+ gboolean success;
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
g_return_val_if_fail (callback != NULL, FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ if (!mm_serial_port_is_open (self)) {
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_NOT_OPEN,
+ "The serial port is not open.");
+ callback (self, error, user_data);
+ g_error_free (error);
+ return FALSE;
+ }
+
if (priv->flash_id > 0) {
error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS,
@@ -1064,11 +1066,13 @@ mm_serial_port_flash (MMSerialPort *self,
return FALSE;
}
- if (!get_speed (self, &cur_speed, &error)) {
+ success = get_speed (self, &cur_speed, &error);
+ if (!success && !ignore_errors) {
callback (self, error, user_data);
g_error_free (error);
return FALSE;
}
+ g_clear_error (&error);
info = g_slice_new0 (FlashInfo);
info->port = self;
@@ -1076,7 +1080,8 @@ mm_serial_port_flash (MMSerialPort *self,
info->callback = callback;
info->user_data = user_data;
- if (!set_speed (self, B0, &error)) {
+ success = set_speed (self, B0, &error);
+ if (!success && !ignore_errors) {
callback (self, error, user_data);
g_error_free (error);
return FALSE;
@@ -1113,12 +1118,54 @@ mm_serial_port_new (const char *name, MMPortType ptype)
NULL));
}
+static gboolean
+ba_equal (gconstpointer v1, gconstpointer v2)
+{
+ const GByteArray *a = v1;
+ const GByteArray *b = v2;
+
+ if (!a && b)
+ return -1;
+ else if (a && !b)
+ return 1;
+ else if (!a && !b)
+ return 0;
+
+ g_assert (a && b);
+ if (a->len < b->len)
+ return -1;
+ else if (a->len > b->len)
+ return 1;
+
+ g_assert (a->len == b->len);
+ return !memcmp (a->data, b->data, a->len);
+}
+
+static guint
+ba_hash (gconstpointer v)
+{
+ /* 31 bit hash function */
+ const GByteArray *array = v;
+ guint32 i, h = (const signed char) array->data[0];
+
+ for (i = 1; i < array->len; i++)
+ h = (h << 5) - h + (const signed char) array->data[i];
+
+ return h;
+}
+
+static void
+ba_free (gpointer v)
+{
+ g_byte_array_free ((GByteArray *) v, TRUE);
+}
+
static void
mm_serial_port_init (MMSerialPort *self)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->reply_cache = g_hash_table_new_full (ba_hash, ba_equal, ba_free, ba_free);
priv->fd = -1;
priv->baud = 57600;
@@ -1128,8 +1175,7 @@ mm_serial_port_init (MMSerialPort *self)
priv->send_delay = 1000;
priv->queue = g_queue_new ();
- priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE);
- priv->response = g_string_sized_new (SERIAL_BUF_SIZE);
+ priv->response = g_byte_array_sized_new (500);
}
static void
@@ -1191,7 +1237,10 @@ get_property (GObject *object, guint prop_id,
static void
dispose (GObject *object)
{
- mm_serial_port_close (MM_SERIAL_PORT (object));
+ if (mm_serial_port_is_open (MM_SERIAL_PORT (object)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (object));
+
+ mm_serial_port_flash_cancel (MM_SERIAL_PORT (object));
G_OBJECT_CLASS (mm_serial_port_parent_class)->dispose (object);
}
@@ -1203,24 +1252,8 @@ finalize (GObject *object)
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
g_hash_table_destroy (priv->reply_cache);
+ g_byte_array_free (priv->response, TRUE);
g_queue_free (priv->queue);
- g_string_free (priv->command, TRUE);
- g_string_free (priv->response, TRUE);
-
- 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);
G_OBJECT_CLASS (mm_serial_port_parent_class)->finalize (object);
}
@@ -1238,6 +1271,9 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
object_class->dispose = dispose;
object_class->finalize = finalize;
+ klass->config_fd = real_config_fd;
+ klass->handle_response = real_handle_response;
+
/* Properties */
g_object_class_install_property
(object_class, PROP_BAUD,
@@ -1278,4 +1314,14 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
"Send delay",
0, G_MAXUINT64, 0,
G_PARAM_READWRITE));
+
+ /* Signals */
+ g_signal_new ("buffer-full",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
}
+
diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h
index 841b4fa..f78f793 100644
--- a/src/mm-serial-port.h
+++ b/src/mm-serial-port.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_SERIAL_PORT_H
@@ -39,67 +39,101 @@
typedef struct _MMSerialPort MMSerialPort;
typedef struct _MMSerialPortClass MMSerialPortClass;
-typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
- GString *response,
- GError **error);
-
-typedef void (*MMSerialUnsolicitedMsgFn) (MMSerialPort *port,
- GMatchInfo *match_info,
- gpointer user_data);
-
-typedef void (*MMSerialResponseFn) (MMSerialPort *port,
- GString *response,
+typedef void (*MMSerialFlashFn) (MMSerialPort *port,
GError *error,
gpointer user_data);
-typedef void (*MMSerialFlashFn) (MMSerialPort *port,
+typedef void (*MMSerialResponseFn) (MMSerialPort *port,
+ GByteArray *response,
GError *error,
gpointer user_data);
+
struct _MMSerialPort {
MMPort parent;
};
struct _MMSerialPortClass {
MMPortClass parent;
+
+ /* Called for subclasses to parse unsolicited responses. If any recognized
+ * unsolicited response is found, it should be removed from the 'response'
+ * byte array before returning.
+ */
+ void (*parse_unsolicited) (MMSerialPort *self, GByteArray *response);
+
+ /* Called to parse the device's response to a command or determine if the
+ * response was an error response. If the response indicates an error, an
+ * appropriate error should be returned in the 'error' argument. The
+ * function should return FALSE if there is not enough data yet to determine
+ * the device's reply (whether success *or* error), and should return TRUE
+ * when the device's response has been recognized and parsed.
+ */
+ gboolean (*parse_response) (MMSerialPort *self,
+ GByteArray *response,
+ GError **error);
+
+ /* Called after parsing to allow the command response to be delivered to
+ * it's callback to be handled. Returns the # of bytes of the response
+ * consumed.
+ */
+ gsize (*handle_response) (MMSerialPort *self,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data);
+
+ /* Called to configure the serial port after it's opened. On error, should
+ * return FALSE and set 'error' as appropriate.
+ */
+ gboolean (*config_fd) (MMSerialPort *self, int fd, GError **error);
+
+ void (*debug_log) (MMSerialPort *self,
+ const char *prefix,
+ const char *buf,
+ gsize len);
+
+ /* Signals */
+ void (*buffer_full) (MMSerialPort *port, const GByteArray *buffer);
};
GType mm_serial_port_get_type (void);
MMSerialPort *mm_serial_port_new (const char *name, MMPortType ptype);
-void mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self,
- GRegex *regex,
- MMSerialUnsolicitedMsgFn callback,
- gpointer user_data,
- GDestroyNotify notify);
+/* Keep in mind that port open/close is refcounted, so ensure that
+ * open/close calls are properly balanced.
+ */
-void mm_serial_port_set_response_parser (MMSerialPort *self,
- MMSerialResponseParserFn fn,
- gpointer user_data,
- GDestroyNotify notify);
+gboolean mm_serial_port_is_open (MMSerialPort *self);
gboolean mm_serial_port_open (MMSerialPort *self,
GError **error);
void mm_serial_port_close (MMSerialPort *self);
+
+void mm_serial_port_close_force (MMSerialPort *self);
+
+gboolean mm_serial_port_flash (MMSerialPort *self,
+ guint32 flash_time,
+ gboolean ignore_errors,
+ MMSerialFlashFn callback,
+ gpointer user_data);
+void mm_serial_port_flash_cancel (MMSerialPort *self);
+
void mm_serial_port_queue_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data);
void mm_serial_port_queue_command_cached (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data);
-gboolean mm_serial_port_flash (MMSerialPort *self,
- guint32 flash_time,
- MMSerialFlashFn callback,
- gpointer user_data);
-void mm_serial_port_flash_cancel (MMSerialPort *self);
-
#endif /* MM_SERIAL_PORT_H */
diff --git a/src/mm-utils.c b/src/mm-utils.c
new file mode 100644
index 0000000..56182c0
--- /dev/null
+++ b/src/mm-utils.c
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "mm-utils.h"
+
+/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
+
+static int hex2num (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+int utils_hex2byte (const char *hex)
+{
+ int a, b;
+ a = hex2num(*hex++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+char *
+utils_hexstr2bin (const char *hex, gsize *out_len)
+{
+ size_t len = strlen (hex);
+ size_t i;
+ int a;
+ const char * ipos = hex;
+ char * buf = NULL;
+ char * opos;
+
+ /* Length must be a multiple of 2 */
+ g_return_val_if_fail ((len % 2) == 0, NULL);
+
+ opos = buf = g_malloc0 ((len / 2) + 1);
+ for (i = 0; i < len; i += 2) {
+ a = utils_hex2byte (ipos);
+ if (a < 0) {
+ g_free (buf);
+ return NULL;
+ }
+ *opos++ = a;
+ ipos += 2;
+ }
+ *out_len = len / 2;
+ return buf;
+}
+
+/* End from hostap */
+
diff --git a/src/mm-utils.h b/src/mm-utils.h
new file mode 100644
index 0000000..79e7827
--- /dev/null
+++ b/src/mm-utils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_UTILS_H
+#define MM_UTILS_H
+
+int utils_hex2byte (const char *hex);
+
+char *utils_hexstr2bin (const char *hex, gsize *out_len);
+
+#endif /* MM_UTILS_H */
+
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index 3d93423..92a7af8 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -18,6 +18,11 @@
#include "mm-modem-helpers.h"
+typedef struct {
+ GPtrArray *solicited_creg;
+ GPtrArray *unsolicited_creg;
+} TestData;
+
#define MM_SCAN_TAG_STATUS "status"
#define MM_SCAN_TAG_OPER_LONG "operator-long"
#define MM_SCAN_TAG_OPER_SHORT "operator-short"
@@ -35,10 +40,10 @@ typedef struct {
#define ARRAY_LEN(i) (sizeof (i) / sizeof (i[0]))
static void
-test_results (const char *desc,
- const char *reply,
- OperEntry *expected_results,
- guint32 expected_results_len)
+test_cops_results (const char *desc,
+ const char *reply,
+ OperEntry *expected_results,
+ guint32 expected_results_len)
{
guint i;
GError *error = NULL;
@@ -101,7 +106,7 @@ test_cops_response_tm506 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -113,7 +118,7 @@ test_cops_response_gt3gplus (void *f, gpointer d)
{ "1", "Cingular", "Cingular", "310410", "0" },
};
- test_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -126,7 +131,7 @@ test_cops_response_ac881 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -139,7 +144,7 @@ test_cops_response_gtmax36 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -152,7 +157,7 @@ test_cops_response_ac860 (void *f, gpointer d)
{ "1", "Cingular", "Cinglr", "310410", "0" },
};
- test_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -165,7 +170,7 @@ test_cops_response_gtm378 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -177,7 +182,7 @@ test_cops_response_motoc (void *f, gpointer d)
{ "0", "Cingular Wireless", NULL, "310410", NULL },
};
- test_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -189,7 +194,7 @@ test_cops_response_mf627a (void *f, gpointer d)
{ "3", "Voicestream Wireless Corporation", "VSTREAM", "31026", "0" },
};
- test_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -201,7 +206,7 @@ test_cops_response_mf627b (void *f, gpointer d)
{ "3", NULL, NULL, "31026", "0" },
};
- test_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -213,7 +218,7 @@ test_cops_response_e160g (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -226,7 +231,7 @@ test_cops_response_mercury (void *f, gpointer d)
{ "1", "T-Mobile", "TMO", "31026", "0" },
};
- test_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -239,7 +244,7 @@ test_cops_response_quicksilver (void *f, gpointer d)
{ "1", "AT&T", NULL, "310260", "0" },
};
- test_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -251,7 +256,7 @@ test_cops_response_icon225 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -265,7 +270,7 @@ test_cops_response_icon452 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -278,7 +283,7 @@ test_cops_response_f3507g (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "2" }
};
- test_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -291,7 +296,7 @@ test_cops_response_f3607gw (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -304,7 +309,7 @@ test_cops_response_mc8775 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -317,7 +322,7 @@ test_cops_response_n80 (void *f, gpointer d)
{ "1", "Cingular", NULL, "31041", NULL },
};
- test_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -329,7 +334,7 @@ test_cops_response_e1550 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -341,7 +346,7 @@ test_cops_response_mf622 (void *f, gpointer d)
{ "1", NULL, NULL, "310410", "0" },
};
- test_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -354,7 +359,7 @@ test_cops_response_e226 (void *f, gpointer d)
{ "1", NULL, NULL, "310410", "0" },
};
- test_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -367,7 +372,7 @@ test_cops_response_xu870 (void *f, gpointer d)
{ "1", "T-Mobile", "TMO", "31026", "0" },
};
- test_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -380,7 +385,7 @@ test_cops_response_gtultraexpress (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -392,7 +397,7 @@ test_cops_response_n2720 (void *f, gpointer d)
{ "1", "AT&T", NULL, "310410", "0" },
};
- test_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -405,7 +410,28 @@ test_cops_response_gobi (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected));
+}
+
+static void
+test_cops_response_sek600i (void *f, gpointer d)
+{
+ /* Phone is stupid enough to support 3G but not report cell technology,
+ * mixing together 2G and 3G cells without any way of distinguishing
+ * which is which...
+ */
+ const char *reply = "+COPS: (2,\"blau\",\"\",\"26203\"),(2,\"blau\",\"\",\"26203\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26202\"),(3,\"\",\"\",\"26207\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26207\")";
+ static OperEntry expected[] = {
+ { "2", "blau", NULL, "26203", NULL },
+ { "2", "blau", NULL, "26203", NULL },
+ { "3", NULL, NULL, "26201", NULL },
+ { "3", NULL, NULL, "26202", NULL },
+ { "3", NULL, NULL, "26207", NULL },
+ { "3", NULL, NULL, "26201", NULL },
+ { "3", NULL, NULL, "26207", NULL },
+ };
+
+ test_cops_results ("Sony-Ericsson K600i", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -432,6 +458,338 @@ test_cops_response_umts_invalid (void *f, gpointer d)
g_assert (error == NULL);
}
+typedef struct {
+ guint32 state;
+ gulong lac;
+ gulong ci;
+ gint act;
+
+ guint regex_num;
+ gboolean cgreg;
+} CregResult;
+
+static void
+test_creg_match (const char *test,
+ gboolean solicited,
+ const char *reply,
+ TestData *data,
+ const CregResult *result)
+{
+ int i;
+ GMatchInfo *info = NULL;
+ guint32 state = 0;
+ gulong lac = 0, ci = 0;
+ gint access_tech = -1;
+ GError *error = NULL;
+ gboolean success, cgreg = FALSE;
+ guint regex_num = 0;
+ GPtrArray *array;
+
+ g_assert (reply);
+ g_assert (test);
+ g_assert (data);
+ g_assert (result);
+
+ g_print ("\nTesting %s +CREG %s response...\n",
+ test,
+ solicited ? "solicited" : "unsolicited");
+
+ array = solicited ? data->solicited_creg : data->unsolicited_creg;
+ for (i = 0; i < array->len; i++) {
+ GRegex *r = g_ptr_array_index (array, i);
+
+ if (g_regex_match (r, reply, 0, &info)) {
+ regex_num = i + 1;
+ break;
+ }
+ g_match_info_free (info);
+ info = NULL;
+ }
+
+ g_assert (info != NULL);
+ g_assert (regex_num == result->regex_num);
+
+ success = mm_gsm_parse_creg_response (info, &state, &lac, &ci, &access_tech, &cgreg, &error);
+ g_assert (success);
+ g_assert (error == NULL);
+ g_assert (state == result->state);
+ g_assert (lac == result->lac);
+ g_assert (ci == result->ci);
+ g_assert (access_tech == result->act);
+ g_assert (cgreg == result->cgreg);
+}
+
+static void
+test_creg1_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 1,3";
+ const CregResult result = { 3, 0, 0, -1 , 2, FALSE};
+
+ test_creg_match ("CREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_creg1_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 3\r\n";
+ const CregResult result = { 3, 0, 0, -1 , 1, FALSE};
+
+ test_creg_match ("CREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_mercury_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 0,1,84CD,00D30173";
+ const CregResult result = { 1, 0x84cd, 0xd30173, -1 , 4, FALSE};
+
+ test_creg_match ("Sierra Mercury CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_mercury_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 1,84CD,00D30156\r\n";
+ const CregResult result = { 1, 0x84cd, 0xd30156, -1 , 3, FALSE};
+
+ test_creg_match ("Sierra Mercury CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_sek850i_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,\"CE00\",\"01CEAD8F\"";
+ const CregResult result = { 1, 0xce00, 0x01cead8f, -1 , 4, FALSE};
+
+ test_creg_match ("Sony Ericsson K850i CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_sek850i_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 1,\"CE00\",\"00005449\"\r\n";
+ const CregResult result = { 1, 0xce00, 0x5449, -1 , 3, FALSE};
+
+ test_creg_match ("Sony Ericsson K850i CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_solicited_unregistered (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,0,00,0";
+ const CregResult result = { 0, 0, 0, -1 , 4, FALSE};
+
+ test_creg_match ("Huawei E160G unregistered CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,8BE3,2BAF";
+ const CregResult result = { 1, 0x8be3, 0x2baf, -1 , 4, FALSE};
+
+ test_creg_match ("Huawei E160G CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,8BE3,2BAF\r\n";
+ const CregResult result = { 2, 0x8be3, 0x2baf, -1 , 3, FALSE};
+
+ test_creg_match ("Huawei E160G CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_tm506_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,\"8BE3\",\"00002BAF\"";
+ const CregResult result = { 1, 0x8BE3, 0x2BAF, -1 , 4, FALSE};
+
+ /* Test leading zeros in the CI */
+ test_creg_match ("Sony Ericsson TM-506 CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_xu870_unsolicited_unregistered (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,,\r\n";
+ const CregResult result = { 2, 0, 0, -1 , 3, FALSE};
+
+ test_creg_match ("Novatel XU870 unregistered CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg1_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CGREG: 1,3";
+ const CregResult result = { 3, 0, 0, -1 , 2, TRUE};
+
+ test_creg_match ("CGREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg1_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 3\r\n";
+ const CregResult result = { 3, 0, 0, -1 , 1, TRUE};
+
+ test_creg_match ("CGREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg2_f3607gw_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CGREG: 2,1,\"8BE3\",\"00002B5D\",3";
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, 3 , 6, TRUE};
+
+ test_creg_match ("Ericsson F3607gw CGREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg2_f3607gw_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 1,\"8BE3\",\"00002B5D\",3\r\n";
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, 3 , 5, TRUE};
+
+ test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_md400_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,5,\"0502\",\"0404736D\"\r\n";
+ const CregResult result = { 5, 0x0502, 0x0404736D, -1 , 4, FALSE};
+
+ test_creg_match ("Sony-Ericsson MD400 CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg2_md400_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 5,\"0502\",\"0404736D\",2\r\n";
+ const CregResult result = { 5, 0x0502, 0x0404736D, 2, 5, TRUE};
+
+ test_creg_match ("Sony-Ericsson MD400 CGREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg_cgreg_multi_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 5\r\n\r\n+CGREG: 0\r\n";
+ const CregResult result = { 5, 0, 0, -1, 1, FALSE};
+
+ test_creg_match ("Multi CREG/CGREG", FALSE, reply, data, &result);
+}
+
+static void
+test_creg_cgreg_multi2_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 0\r\n\r\n+CREG: 5\r\n";
+ const CregResult result = { 0, 0, 0, -1, 1, TRUE};
+
+ test_creg_match ("Multi CREG/CGREG #2", FALSE, reply, data, &result);
+}
+
+static void
+test_cscs_icon225_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\")\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == (MM_MODEM_CHARSET_IRA |
+ MM_MODEM_CHARSET_GSM |
+ MM_MODEM_CHARSET_UCS2));
+}
+
+static void
+test_cscs_sierra_mercury_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\",\"PCCP437\")\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == (MM_MODEM_CHARSET_IRA |
+ MM_MODEM_CHARSET_GSM |
+ MM_MODEM_CHARSET_UCS2 |
+ MM_MODEM_CHARSET_PCCP437));
+}
+
+static void
+test_cscs_buslink_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: (\"8859-1\",\"ASCII\",\"GSM\",\"UCS2\",\"UTF8\")\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == (MM_MODEM_CHARSET_8859_1 |
+ MM_MODEM_CHARSET_IRA |
+ MM_MODEM_CHARSET_GSM |
+ MM_MODEM_CHARSET_UCS2 |
+ MM_MODEM_CHARSET_UTF8));
+}
+
+static void
+test_cscs_blackberry_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: \"IRA\"\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == MM_MODEM_CHARSET_IRA);
+}
+
+static TestData *
+test_data_new (void)
+{
+ TestData *data;
+
+ data = g_malloc0 (sizeof (TestData));
+ data->solicited_creg = mm_gsm_creg_regex_get (TRUE);
+ data->unsolicited_creg = mm_gsm_creg_regex_get (FALSE);
+ return data;
+}
+
+static void
+test_data_free (TestData *data)
+{
+ mm_gsm_creg_regex_destroy (data->solicited_creg);
+ mm_gsm_creg_regex_destroy (data->unsolicited_creg);
+ g_free (data);
+}
+
typedef void (*TCFunc)(void);
@@ -440,10 +798,13 @@ typedef void (*TCFunc)(void);
int main (int argc, char **argv)
{
GTestSuite *suite;
+ TestData *data;
+ gint result;
g_test_init (&argc, &argv, NULL);
suite = g_test_get_root ();
+ data = test_data_new ();
g_test_suite_add (suite, TESTCASE (test_cops_response_tm506, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gt3gplus, NULL));
@@ -470,10 +831,42 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cops_response_gtultraexpress, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_n2720, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gobi, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cops_response_sek600i, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gsm_invalid, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_umts_invalid, NULL));
- return g_test_run ();
+ g_test_suite_add (suite, TESTCASE (test_creg1_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg1_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_mercury_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_mercury_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited_unregistered, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_tm506_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_xu870_unsolicited_unregistered, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_md400_unsolicited, data));
+
+ g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_md400_unsolicited, data));
+
+ g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi2_unsolicited, data));
+
+ g_test_suite_add (suite, TESTCASE (test_cscs_icon225_support_response, data));
+ g_test_suite_add (suite, TESTCASE (test_cscs_sierra_mercury_support_response, data));
+ g_test_suite_add (suite, TESTCASE (test_cscs_buslink_support_response, data));
+ g_test_suite_add (suite, TESTCASE (test_cscs_blackberry_support_response, data));
+
+ result = g_test_run ();
+
+ test_data_free (data);
+
+ return result;
}