From 10d15c65fd10614c4085af390a9fbf390ab958b7 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Wed, 5 Feb 2014 08:38:24 +0100 Subject: Imported Upstream version 0.4+git.20110429t103114.863dbca --- Makefile.am | 2 +- configure.ac | 20 +- ...org.freedesktop.ModemManager.Modem.Gsm.Card.xml | 2 +- ....freedesktop.ModemManager.Modem.Gsm.Network.xml | 12 +- .../org.freedesktop.ModemManager.Modem.Gsm.SMS.xml | 1 + .../org.freedesktop.ModemManager.Modem.Gsm.xml | 3 + libqcdm/autogen.sh | 22 + libqcdm/configure.ac | 48 + marshallers/mm-marshal.list | 2 +- plugins/77-mm-ericsson-mbm.rules | 1 + plugins/77-mm-simtech-port-types.rules | 7 +- plugins/Makefile.am | 44 +- plugins/mm-modem-hso.c | 2 +- plugins/mm-modem-huawei-gsm.c | 98 +- plugins/mm-modem-icera.c | 56 +- plugins/mm-modem-icera.h | 8 +- plugins/mm-modem-samsung-gsm.c | 679 ++++++++++++ plugins/mm-modem-samsung-gsm.h | 47 + plugins/mm-modem-wavecom-gsm.c | 1099 ++++++++++++++++++++ plugins/mm-modem-wavecom-gsm.h | 44 + plugins/mm-modem-zte.c | 49 +- plugins/mm-plugin-anydata.c | 2 +- plugins/mm-plugin-generic.c | 2 +- plugins/mm-plugin-gobi.c | 2 +- plugins/mm-plugin-hso.c | 2 +- plugins/mm-plugin-huawei.c | 230 ++-- plugins/mm-plugin-linktop.c | 2 +- plugins/mm-plugin-longcheer.c | 2 +- plugins/mm-plugin-mbm.c | 2 +- plugins/mm-plugin-moto-c.c | 2 +- plugins/mm-plugin-nokia.c | 2 +- plugins/mm-plugin-novatel.c | 2 +- plugins/mm-plugin-option.c | 2 +- plugins/mm-plugin-samsung.c | 157 +++ plugins/mm-plugin-samsung.h | 45 + plugins/mm-plugin-sierra.c | 2 +- plugins/mm-plugin-simtech.c | 2 +- plugins/mm-plugin-wavecom.c | 162 +++ plugins/mm-plugin-wavecom.h | 47 + plugins/mm-plugin-x22x.c | 2 +- plugins/mm-plugin-zte.c | 24 +- policy/org.freedesktop.modem-manager.policy.in | 2 +- src/mm-charsets.c | 9 +- src/mm-charsets.h | 2 +- src/mm-generic-gsm.c | 556 +++++++++- src/mm-generic-gsm.h | 2 + src/mm-log.c | 16 +- src/mm-modem-base.c | 6 +- src/mm-modem-cdma.c | 12 + src/mm-modem-gsm-network.c | 31 +- src/mm-modem-gsm-sms.c | 153 ++- src/mm-modem-gsm-sms.h | 47 + src/mm-modem-gsm.h | 3 +- src/mm-modem-helpers.c | 2 +- src/mm-modem.c | 2 +- src/mm-modem.h | 2 +- src/mm-plugin-base.c | 125 ++- src/mm-plugin-base.h | 29 +- src/mm-serial-parsers.c | 87 +- src/mm-serial-parsers.h | 16 +- src/mm-serial-port.c | 206 ++-- src/mm-serial-port.h | 13 +- src/mm-utils.c | 17 + src/mm-utils.h | 2 + src/tests/test-charsets.c | 11 +- src/tests/test-modem-helpers.c | 11 + test/Makefile.am | 3 +- test/send-pin.py | 65 ++ 68 files changed, 3916 insertions(+), 453 deletions(-) create mode 100755 libqcdm/autogen.sh create mode 100644 libqcdm/configure.ac create mode 100755 plugins/mm-modem-samsung-gsm.c create mode 100755 plugins/mm-modem-samsung-gsm.h create mode 100644 plugins/mm-modem-wavecom-gsm.c create mode 100644 plugins/mm-modem-wavecom-gsm.h create mode 100755 plugins/mm-plugin-samsung.c create mode 100755 plugins/mm-plugin-samsung.h create mode 100644 plugins/mm-plugin-wavecom.c create mode 100644 plugins/mm-plugin-wavecom.h create mode 100755 test/send-pin.py diff --git a/Makefile.am b/Makefile.am index 4132bc9..309bda1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,7 +55,7 @@ include/mm-modem.h: $(XMLS) introspection/all.xml header-generator.xsl $(edit) $< >$@ xmldir = $(datadir)/dbus-1/interfaces -xml_DATA = $(XMLS) +xml_DATA = $(filter-out introspection/all.xml, $(XMLS)) edit = @sed \ -e 's|@sbindir[@]|$(sbindir)|g' \ diff --git a/configure.ac b/configure.ac index 4f8b94f..2567fa0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ -AC_PREREQ(2.52) +AC_PREREQ([2.60]) -AC_INIT(ModemManager, 0.4, dcbw@redhat.com, ModemManager) -AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2]) +AC_INIT([ModemManager],[0.4],[dcbw@redhat.com],[ModemManager]) +AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2 -Wno-portability]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE @@ -9,14 +9,17 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS(config.h) +dnl Define system extensions for various things like strcasestr() +AC_USE_SYSTEM_EXTENSIONS + dnl Required programs AC_PROG_CC AM_PROG_CC_C_O AC_PROG_INSTALL -AC_PROG_LIBTOOL -dnl Define _GNU_SOURCE for various things like strcasestr() -AC_GNU_SOURCE +dnl Initialize libtool +LT_PREREQ([2.2]) +LT_INIT dnl dnl translation support @@ -25,7 +28,6 @@ GETTEXT_PACKAGE=ModemManager AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package]) IT_PROG_INTLTOOL([0.35.0]) -AM_GLIB_GNU_GETTEXT PKG_CHECK_MODULES(MM, dbus-glib-1 >= 0.86 glib-2.0 >= 2.18 gmodule-2.0 gobject-2.0) @@ -68,6 +70,8 @@ case $with_polkit in # Check for polkit_authority_get_sync() AC_CHECK_LIB([polkit-gobject-1], [polkit_authority_get_sync], ac_have_pk_auth_get_sync="1", ac_have_pk_auth_get_sync="0") AC_DEFINE_UNQUOTED(HAVE_POLKIT_AUTHORITY_GET_SYNC, $ac_have_pk_auth_get_sync, [Define if you have a polkit with polkit_authority_get_sync()]) + + AM_GLIB_GNU_GETTEXT ;; *) with_polkit=no @@ -160,6 +164,4 @@ echo Building PPP-enabled tests: ${have_pppd_headers} echo echo Building with PolicyKit support: ${with_polkit} echo -echo Building with Location API support: ${with_location_api} -echo diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml index d481157..bf33f4c 100644 --- a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml @@ -62,7 +62,7 @@ - Send the PIN (or PUK) to unlock the SIM card. + Send the PIN to unlock the SIM card. diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml index 7c26681..4a8e803 100644 --- a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml @@ -87,26 +87,26 @@ - Sets the band the device is allowed to use when connecting to a mobile network. + Sets the bands the device is allowed to use when connecting to a mobile network. - The desired band. Only one band may be specified, and may not be UNKNOWN. + The desired bands, as a set of flags. - Returns the current band the device is using. (Note for plugin writers: returned value must not be ANY) + Returns the current bands the device is using. (Note for plugin writers: returned value must not be ANY) - The current band. + The current bands, as a set of flags. @@ -160,7 +160,7 @@
  • Current operator code of the operator to which the mobile is currently registered. Returned in the format "MCCMNC", where MCC - is the three-digit ITU E.212 Mobile Country Code and MNC is the + is the three-digit ITU E.212 Mobile Country Code and MNC is the two- or three-digit GSM Mobile Network Code. If the MCC and MNC are not known or the mobile is not registered to a mobile network, this value should be a zero-length (blank) string. e.g. "31026" @@ -233,7 +233,7 @@ Current operator code of the operator to which the mobile is currently registered. Returned in the format "MCCMNC", where MCC - is the three-digit ITU E.212 Mobile Country Code and MNC is the + is the three-digit ITU E.212 Mobile Country Code and MNC is the two- or three-digit GSM Mobile Network Code. If the MCC and MNC are not known or the mobile is not registered to a mobile network, this value should be a zero-length (blank) string. e.g. "31026" or diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml index 081ecc5..15953b8 100644 --- a/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml @@ -36,6 +36,7 @@ validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) class : uint (0..3) - Message importance and location (optional) completed: boolean - Whether all message parts have been received or not (optional) + index : uint - Index of message (for Get and Delete) (optional) diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml index 78da9a4..75cdeb0 100644 --- a/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml @@ -100,6 +100,9 @@ WCDMA 3GPP UMTS 1900 MHz (Class II) + + WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) + diff --git a/libqcdm/autogen.sh b/libqcdm/autogen.sh new file mode 100755 index 0000000..e3b537c --- /dev/null +++ b/libqcdm/autogen.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +# NOTE +# This autogen.sh is only used when building libqcdm separately from ModemManager + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. +REQUIRED_AUTOMAKE_VERSION=1.7 +PKG_NAME=libqcdm + +(test -f $srcdir/configure.ac \ + && test -f $srcdir/src/com.c) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level $PKG_NAME directory" + exit 1 +} + +(cd $srcdir; + autoreconf --install --symlink && + autoreconf && + ./configure --enable-maintainer-mode $@ +) diff --git a/libqcdm/configure.ac b/libqcdm/configure.ac new file mode 100644 index 0000000..afd6851 --- /dev/null +++ b/libqcdm/configure.ac @@ -0,0 +1,48 @@ +# NOTE +# This configure.ac is only used when building libqcdm separately from +# ModemManager. +# + +AC_PREREQ(2.52) + +AC_INIT(libqcdm, 0.1, dcbw@redhat.com, libqcdm) +AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AM_MAINTAINER_MODE + +AC_CONFIG_MACRO_DIR([m4]) + +AC_CONFIG_HEADERS(config.h) + +dnl Required programs +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_INSTALL +AC_PROG_LIBTOOL + +dnl Define _GNU_SOURCE for various things like strcasestr() +AC_GNU_SOURCE + +PKG_CHECK_MODULES(MM, glib-2.0 >= 2.18) + +dnl +dnl Tests +dnl +AC_ARG_WITH(tests, AS_HELP_STRING([--with-tests], [Build ModemManager tests])) +AM_CONDITIONAL(WITH_TESTS, test "x$with_tests" = "xyes") +case $with_tests in + yes) + with_tests=yes + ;; + *) + with_tests=no + ;; +esac + +AC_CONFIG_FILES([ +Makefile +src/Makefile +tests/Makefile +]) +AC_OUTPUT + diff --git a/marshallers/mm-marshal.list b/marshallers/mm-marshal.list index 81fcadb..f3504b9 100644 --- a/marshallers/mm-marshal.list +++ b/marshallers/mm-marshal.list @@ -7,4 +7,4 @@ VOID:UINT,UINT,UINT VOID:STRING,BOXED VOID:POINTER,UINT VOID:STRING,BOXED,BOXED - +VOID:UINT,UINT,BOXED diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules index 51cc06f..8c03acf 100644 --- a/plugins/77-mm-ericsson-mbm.rules +++ b/plugins/77-mm-ericsson-mbm.rules @@ -28,6 +28,7 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson F5521gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{ID_MM_ERICSSON_MBM}="1" # Sony-Ericsson MD300 ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1" diff --git a/plugins/77-mm-simtech-port-types.rules b/plugins/77-mm-simtech-port-types.rules index 9ec047c..3848f89 100644 --- a/plugins/77-mm-simtech-port-types.rules +++ b/plugins/77-mm-simtech-port-types.rules @@ -3,6 +3,7 @@ # Simtech makes modules that other companies rebrand, like: # # A-LINK 3GU +# SCT UM300 # # Most of these values were scraped from various SimTech-based Windows # driver .inf files. *mdm.inf lists the main command ports, while @@ -18,12 +19,16 @@ GOTO="mm_simtech_port_types_end" LABEL="mm_alink_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" +ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="cefe", ENV{ID_MM_SIMTECH_TAGGED}="1" + ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" ATTRS{idProduct}=="9200", ENV{ID_MM_SIMTECH_TAGGED}="1" GOTO="mm_simtech_port_types_end" - LABEL="mm_simtech_port_types_end" diff --git a/plugins/Makefile.am b/plugins/Makefile.am index dd58b94..8eaffb4 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -35,7 +35,9 @@ pkglib_LTLIBRARIES = \ libmm-plugin-anydata.la \ libmm-plugin-simtech.la \ libmm-plugin-x22x.la \ - libmm-plugin-linktop.la + libmm-plugin-linktop.la \ + libmm-plugin-samsung.la \ + libmm-plugin-wavecom.la # Generic @@ -187,6 +189,24 @@ libmm_plugin_sierra_la_LDFLAGS = \ -module \ -avoid-version +# Wavecom (Sierra Airlink) + +libmm_plugin_wavecom_la_SOURCES = \ + mm-plugin-wavecom.c \ + mm-plugin-wavecom.h \ + mm-modem-wavecom-gsm.c \ + mm-modem-wavecom-gsm.h + +libmm_plugin_wavecom_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_wavecom_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + # Novatel libmm_plugin_novatel_la_SOURCES = \ @@ -241,10 +261,12 @@ libmm_plugin_zte_la_CPPFLAGS = \ libmm_plugin_zte_la_LDFLAGS = \ $(GUDEV_LDFLAGS) \ - $(builddir)/libicera-utils.la \ -module \ -avoid-version +libmm_plugin_zte_la_LIBADD = \ + $(builddir)/libicera-utils.la + # Longcheer (and rebranded dongles) libmm_plugin_longcheer_la_SOURCES = \ @@ -335,6 +357,24 @@ libmm_plugin_linktop_la_LDFLAGS = \ -module \ -avoid-version +# Samsung modem + +libmm_plugin_samsung_la_SOURCES = \ + mm-plugin-samsung.c \ + mm-plugin-samsung.h \ + mm-modem-samsung-gsm.c \ + mm-modem-samsung-gsm.h + +libmm_plugin_samsung_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_samsung_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + udevrulesdir = $(UDEV_BASE_DIR)/rules.d udevrules_DATA = \ 77-mm-ericsson-mbm.rules \ diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c index ff0264a..7081666 100644 --- a/plugins/mm-modem-hso.c +++ b/plugins/mm-modem-hso.c @@ -750,7 +750,7 @@ grab_port (MMModem *modem, goto out; if (MM_IS_AT_SERIAL_PORT (port)) { - g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 10000, NULL); + g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 0, NULL); if (ptype == MM_PORT_TYPE_PRIMARY) { GRegex *regex; diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c index 3fc9ae2..df58d53 100644 --- a/plugins/mm-modem-huawei-gsm.c +++ b/plugins/mm-modem-huawei-gsm.c @@ -85,9 +85,7 @@ static BandTable bands[] = { /* 2G second */ { MM_MODEM_GSM_BAND_DCS, 0x00000080 }, { MM_MODEM_GSM_BAND_EGSM, 0x00000300 }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */ - { MM_MODEM_GSM_BAND_PCS, 0x00200000 }, - /* And ANY last since it's most inclusive */ - { MM_MODEM_GSM_BAND_ANY, 0x3FFFFFFF }, + { MM_MODEM_GSM_BAND_PCS, 0x00200000 } }; static gboolean @@ -95,13 +93,19 @@ band_mm_to_huawei (MMModemGsmBand band, guint32 *out_huawei) { int i; - for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { - if (bands[i].mm == band) { - *out_huawei = bands[i].huawei; - return TRUE; + /* Treat ANY as a special case: All huawei flags enabled */ + if (band == MM_MODEM_GSM_BAND_ANY) { + *out_huawei = 0x3FFFFFFF; + return TRUE; + } + + *out_huawei = 0; + for (i = 0; i < G_N_ELEMENTS (bands); i++) { + if (bands[i].mm & band) { + *out_huawei |= bands[i].huawei; } } - return FALSE; + return (*out_huawei > 0 ? TRUE : FALSE); } static gboolean @@ -109,16 +113,13 @@ band_huawei_to_mm (guint32 huawei, MMModemGsmBand *out_mm) { int i; - for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { - /* The dongle returns a bitfield, but since we don't support that - * yet in MM, take the "best" band and return it. - */ + *out_mm = 0; + for (i = 0; i < G_N_ELEMENTS (bands); i++) { if (bands[i].huawei & huawei) { - *out_mm = bands[i].mm; - return TRUE; + *out_mm |= bands[i].mm; } } - return FALSE; + return (*out_mm > 0 ? TRUE : FALSE); } /*****************************************************************************/ @@ -687,6 +688,71 @@ handle_status_change (MMAtSerialPort *port, /*****************************************************************************/ +static void +do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + if (!error) { + MMAtSerialPort *primary; + + /* Enable unsolicited result codes */ + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_at_serial_port_queue_command (primary, "^CURC=1", 5, NULL, NULL); + } + + /* Chain up to parent */ + MM_GENERIC_GSM_CLASS (mm_modem_huawei_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info); +} + +/*****************************************************************************/ + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) + +{ + MMModem *parent_modem_iface; + DisableInfo *info = user_data; + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, info->callback, info->user_data); + g_free (info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMAtSerialPort *primary; + DisableInfo *info; + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Turn off unsolicited responses */ + mm_at_serial_port_queue_command (primary, "^CURC=0", 5, disable_unsolicited_done, info); +} + +/*****************************************************************************/ + static gboolean grab_port (MMModem *modem, const char *subsys, @@ -766,6 +832,7 @@ static void modem_init (MMModem *modem_class) { modem_class->grab_port = grab_port; + modem_class->disable = disable; } static void @@ -798,5 +865,6 @@ mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass) gsm_class->set_allowed_mode = set_allowed_mode; gsm_class->get_allowed_mode = get_allowed_mode; gsm_class->get_access_technology = get_access_technology; + gsm_class->do_enable_power_up_done = do_enable_power_up_done; } diff --git a/plugins/mm-modem-icera.c b/plugins/mm-modem-icera.c index 3f71c5b..d34e71e 100644 --- a/plugins/mm-modem-icera.c +++ b/plugins/mm-modem-icera.c @@ -45,7 +45,7 @@ struct _MMModemIceraPrivate { MMModemGsmAccessTech last_act; }; -#define MM_MODEM_ICERA_GET_PRIVATE(m) (MM_MODEM_ICERA_GET_INTERFACE(m)->priv) +#define MM_MODEM_ICERA_GET_PRIVATE(m) (MM_MODEM_ICERA_GET_INTERFACE (m)->get_private(m)) static void get_allowed_mode_done (MMAtSerialPort *port, @@ -172,12 +172,16 @@ static MMModemGsmAccessTech nwstate_to_act (const char *str) { /* small 'g' means CS, big 'G' means PS */ - if (!strcmp (str, "2G-GPRS")) + if (!strcmp (str, "2g")) + return MM_MODEM_GSM_ACCESS_TECH_GSM; + else if (!strcmp (str, "2G-GPRS")) return MM_MODEM_GSM_ACCESS_TECH_GPRS; else if (!strcmp (str, "2G-EDGE")) return MM_MODEM_GSM_ACCESS_TECH_EDGE; else if (!strcmp (str, "3G")) return MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (!strcmp (str, "3g")) + return MM_MODEM_GSM_ACCESS_TECH_UMTS; else if (!strcmp (str, "3G-HSDPA")) return MM_MODEM_GSM_ACCESS_TECH_HSDPA; else if (!strcmp (str, "3G-HSUPA")) @@ -447,10 +451,10 @@ icera_connect_timed_out (gpointer data) } static void -icera_enabled (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +icera_connected (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; @@ -478,7 +482,7 @@ old_context_clear_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; /* Activate the PDP context and start the data session */ - icera_call_control (MM_MODEM_ICERA (info->modem), TRUE, icera_enabled, info); + icera_call_control (MM_MODEM_ICERA (info->modem), TRUE, icera_connected, info); } static void @@ -526,8 +530,8 @@ mm_modem_icera_do_connect (MMModemIcera *self, else { command = g_strdup_printf ("%%IPDPCFG=%d,0,1,\"%s\",\"%s\"", cid, - priv->password ? priv->password : "", - priv->username ? priv->username : ""); + priv->username ? priv->username : "", + priv->password ? priv->password : ""); } @@ -671,7 +675,7 @@ mm_modem_icera_register_unsolicted_handlers (MMModemIcera *self, GRegex *regex; /* %NWSTATE: ,,,, */ - regex = g_regex_new ("\\r\\n%NWSTATE:\\s*(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + regex = g_regex_new ("\\r\\n%NWSTATE:\\s*(-?\\d+),(\\d+),([^,]*),([^,]*),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_at_serial_port_add_unsolicited_msg_handler (port, regex, nwstate_changed, self, NULL); g_regex_unref (regex); @@ -731,14 +735,6 @@ mm_modem_icera_is_icera (MMModemIcera *self, mm_at_serial_port_queue_command (port, "%IPSYS?", 5, is_icera_done, info); } -/****************************************************************/ - -void -mm_modem_icera_prepare (MMModemIcera *self) -{ - self->priv = g_malloc0 (sizeof (MMModemIceraPrivate)); -} - void mm_modem_icera_cleanup (MMModemIcera *self) { @@ -748,20 +744,32 @@ mm_modem_icera_cleanup (MMModemIcera *self) connect_pending_done (self); g_free (priv->username); + priv->username = NULL; g_free (priv->password); + priv->password = NULL; +} - memset (priv, 0, sizeof (MMModemIceraPrivate)); +/****************************************************************/ + +MMModemIceraPrivate * +mm_modem_icera_init_private (void) +{ + return g_malloc0 (sizeof (MMModemIceraPrivate)); +} + +void +mm_modem_icera_dispose_private (MMModemIcera *self) +{ + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + + mm_modem_icera_cleanup (self); + memset (priv, 0, sizeof (*priv)); g_free (priv); } static void mm_modem_icera_init (gpointer g_iface) { - static gboolean initialized = FALSE; - - if (!initialized) { - initialized = TRUE; - } } GType diff --git a/plugins/mm-modem-icera.h b/plugins/mm-modem-icera.h index 71258d2..aefa841 100644 --- a/plugins/mm-modem-icera.h +++ b/plugins/mm-modem-icera.h @@ -37,12 +37,16 @@ typedef struct _MMModemIcera MMModemIcera; struct _MMModemIcera { GTypeInterface g_iface; - MMModemIceraPrivate *priv; + /* Returns the implementing object's pointer to an internal + * MMModemIceraPrivate pointer. + */ + MMModemIceraPrivate * (*get_private) (MMModemIcera *icera); }; GType mm_modem_icera_get_type (void); -void mm_modem_icera_prepare (MMModemIcera *self); +MMModemIceraPrivate *mm_modem_icera_init_private (void); +void mm_modem_icera_dispose_private (MMModemIcera *self); void mm_modem_icera_cleanup (MMModemIcera *self); diff --git a/plugins/mm-modem-samsung-gsm.c b/plugins/mm-modem-samsung-gsm.c new file mode 100755 index 0000000..4902e0a --- /dev/null +++ b/plugins/mm-modem-samsung-gsm.c @@ -0,0 +1,679 @@ +/* -*- 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 - 2011 Red Hat, Inc. + * Copyright (C) 2011 Samsung Electronics, Inc., + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mm-modem-samsung-gsm.h" +#include "mm-modem-simple.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm-card.h" +#include "mm-log.h" +#include "mm-modem-icera.h" +#include "mm-utils.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); +static void modem_simple_init (MMModemSimple *class); +static void modem_gsm_card_init (MMModemGsmCard *class); +static void modem_icera_init (MMModemIcera *icera_class); + +G_DEFINE_TYPE_EXTENDED (MMModemSamsungGsm, mm_modem_samsung_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_ICERA, modem_icera_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) + +#define MM_MODEM_SAMSUNG_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsmPrivate)) + +typedef struct { + gboolean disposed; + + MMModemIceraPrivate *icera; + char *band; +} MMModemSamsungGsmPrivate; + +#define IPDPADDR_TAG "%IPDPADDR: " + + +MMModem * +mm_modem_samsung_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + MMModem *modem; + + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + modem = MM_MODEM (g_object_new (MM_TYPE_MODEM_SAMSUNG_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP, + NULL)); + if (modem) + MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (modem)->icera = mm_modem_icera_init_private (); + + return modem; +} + +typedef struct { + MMModemGsmBand mm; + char band[50]; +} BandTable; + +static BandTable bands[12] = { + /* Sort 3G first since it's preferred */ + { MM_MODEM_GSM_BAND_U2100, "FDD_BAND_I" }, + { MM_MODEM_GSM_BAND_U1900, "FDD_BAND_II" }, + { MM_MODEM_GSM_BAND_U1800, "FDD_BAND_III" }, + { MM_MODEM_GSM_BAND_U17IV, "FDD_BAND_IV" }, + { MM_MODEM_GSM_BAND_U850, "FDD_BAND_V" }, + { MM_MODEM_GSM_BAND_U800, "FDD_BAND_VI" }, + { MM_MODEM_GSM_BAND_U900, "FDD_BAND_VIII" }, + { MM_MODEM_GSM_BAND_G850, "G850" }, + /* 2G second */ + { MM_MODEM_GSM_BAND_DCS, "DCS" }, + { MM_MODEM_GSM_BAND_EGSM, "EGSM" }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */ + { MM_MODEM_GSM_BAND_PCS, "PCS" }, + /* And ANY last since it's most inclusive */ + { MM_MODEM_GSM_BAND_ANY, "ANY" }, +}; + +static gboolean +band_mm_to_samsung (MMModemGsmBand band, MMModemGsmNetwork *modem) +{ + int i; + MMModemSamsungGsmPrivate *priv = MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (modem); + + for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { + if (bands[i].mm == band) { + priv->band = bands[i].band; + return TRUE; + } + } + return FALSE; +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + mm_modem_icera_get_allowed_mode (MM_MODEM_ICERA (gsm), callback, user_data); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + mm_modem_icera_set_allowed_mode (MM_MODEM_ICERA (gsm), mode, callback, user_data); +} + +static void +set_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_band (MMModemGsmNetwork *modem, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + MMModemSamsungGsmPrivate *priv = MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (modem); + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* TODO: Check how to pass more than one band in the same AT%%IPBM command */ + if (!utils_check_for_single_value (band)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Cannot set more than one band."); + mm_callback_info_schedule (info); + } else if (!band_mm_to_samsung (band, modem)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band."); + mm_callback_info_schedule (info); + } else { + mm_callback_info_set_data (info, "band", g_strdup(priv->band), NULL); + command = g_strdup_printf ("AT%%IPBM=\"%s\",1", priv->band); + mm_at_serial_port_queue_command (port, command, 3, set_band_done, info); + g_free (command); + priv->band = NULL; + } +} + +static gboolean +parse_ipbm (const char *reply, MMModemGsmBand *band) +{ + int enable[12]; + + g_return_val_if_fail (band != NULL, FALSE); + g_return_val_if_fail (reply != NULL, FALSE); + + if (sscanf (reply, "\"ANY\": %d\r\n\"EGSM\": %d\r\n\"DCS\": %d\r\n\"PCS\": %d\r\n\"G850\": %d\r\n\"FDD_BAND_I\": %d\r\n\"FDD_BAND_II\": %d\r\n\"FDD_BAND_III\": %d\r\n\"FDD_BAND_IV\": %d\r\n\"FDD_BAND_V\": %d\r\n\"FDD_BAND_VI\": %d\r\n\"FDD_BAND_VIII\": %d", &enable[0], &enable[1], &enable[2], &enable[3], &enable[4], &enable[5], &enable[6], &enable[7], &enable[8], &enable[9], &enable[10], &enable[11]) != 12) + return FALSE; + + *band = 0; + if (enable[5] == 1) + *band |= MM_MODEM_GSM_BAND_U2100; + if (enable[6] == 1) + *band |= MM_MODEM_GSM_BAND_U1900; + if (enable[7] == 1) + *band |= MM_MODEM_GSM_BAND_U1800; + if (enable[8] == 1) + *band |= MM_MODEM_GSM_BAND_U17IV; + if (enable[9] == 1) + *band |= MM_MODEM_GSM_BAND_U850; + if (enable[10] == 1) + *band |= MM_MODEM_GSM_BAND_U800; + if (enable[11] == 1) + *band |= MM_MODEM_GSM_BAND_U900; + if (enable[1] == 1) + *band |= MM_MODEM_GSM_BAND_EGSM; + if (enable[2] == 1) + *band |= MM_MODEM_GSM_BAND_DCS; + if (enable[3] == 1) + *band |= MM_MODEM_GSM_BAND_PCS; + if (enable[4] == 1) + *band |= MM_MODEM_GSM_BAND_G850; + + return (*band > 0 ? TRUE : FALSE); +} + +static void +get_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_UNKNOWN; + + if (error) + info->error = g_error_copy (error); + else if (parse_ipbm (response->str, &mm_band)) + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + + mm_callback_info_schedule (info); +} + +static void +get_band (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "AT%IPBM?", 3, get_band_done, info); +} + +static gboolean +parse_samsung_num (const char *str, guint32 *out_num, guint32 min, guint32 max) +{ + unsigned long int tmp; + + if (!str || !strlen (str)) + return FALSE; + + errno = 0; + tmp = strtoul (str, NULL, 10); + if (errno != 0 || tmp < min || tmp > max) + return FALSE; + *out_num = (guint32) tmp; + return TRUE; +} + +static void +send_samsung_pinnum_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GRegex *r = NULL; + GMatchInfo *match_info = NULL; + const char *pin_type; + guint32 attempts_left = 0; + char *str = NULL; + guint32 num = 0; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + pin_type = mm_callback_info_get_data (info, "pin_type"); + + r = g_regex_new ("\\%PINNUM:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse %PINNUM results (error creating regex)."); + goto done; + } + + if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse %PINNUM results (match failed)."); + goto done; + } + + if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN)) + num = 1; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK)) + num = 2; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) + num = 3; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2)) + num = 4; + else { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type"); + } + + if (num > 0) { + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + if (str) { + success = parse_samsung_num (str, &attempts_left, 0, 10); + g_free (str); + } + + if (!success) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse %PINNUM results (missing or invalid match info)."); + } + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL); + + g_match_info_free (match_info); + +done: + if (r) + g_regex_unref (r); + mm_serial_port_close (MM_SERIAL_PORT (port)); + mm_callback_info_schedule (info); +} + +static void +reset (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (port) + mm_at_serial_port_queue_command (port, "%IRESET", 3, NULL, NULL); + + mm_callback_info_schedule (info); +} + +static void +get_unlock_retries (MMModemGsmCard *modem, + const char *pin_type, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + mm_dbg ("pin type '%s'", pin_type); + + /* Ensure we have a usable port to use for the command */ + 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; + } + + /* if the modem have not yet been enabled we need to make sure echoing is turned off */ + mm_at_serial_port_queue_command (port, "E0", 3, NULL, NULL); + mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free); + mm_at_serial_port_queue_command (port, "%PINNUM?", 3, send_samsung_pinnum_done, info); + +} + +static void +get_access_technology (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + mm_modem_icera_get_access_technology (MM_MODEM_ICERA (gsm), callback, user_data); +} + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) + +{ + MMModem *parent_modem_iface; + DisableInfo *info = user_data; + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, info->callback, info->user_data); + g_free (info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMAtSerialPort *primary; + DisableInfo *info; + + mm_modem_icera_cleanup (MM_MODEM_ICERA (modem)); + mm_modem_icera_change_unsolicited_messages (MM_MODEM_ICERA (modem), FALSE); + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Random command to ensure unsolicited message disable completes */ + mm_at_serial_port_queue_command (primary, "AT+CFUN=0", 5, disable_unsolicited_done, info); +} + +static void +init_all_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (info->modem); + + if (!error) + mm_modem_icera_change_unsolicited_messages (MM_MODEM_ICERA (self), TRUE); + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); +} + +static void +init2_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (info->modem); + + if (error) + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); + else { + /* Finish the initialization */ + mm_at_serial_port_queue_command (port, "E0 V1 X4 &C1", 3, init_all_done, info); + } +} + +static void +init_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (info->modem); + + if (error) + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); + else { + /* Power up the modem */ + mm_at_serial_port_queue_command (port, "+CMEE=1", 2, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CFUN=1", 10, init2_done, info); + } +} + +static void +do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + primary = mm_generic_gsm_get_at_port (modem, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_at_serial_port_queue_command (primary, "Z E0 V1", 3, init_done, info); +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + mm_modem_icera_do_connect (MM_MODEM_ICERA (modem), number, callback, user_data); +} + +static void +do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) +{ + mm_modem_icera_do_disconnect (gsm, cid, callback, user_data); +} + +static void +get_ip4_config (MMModem *modem, + MMModemIp4Fn callback, + gpointer user_data) +{ + mm_modem_icera_get_ip4_config (MM_MODEM_ICERA (modem), callback, user_data); +} + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMModemSimple *parent_iface; + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + /* Let icera cache user & password */ + mm_modem_icera_simple_connect (MM_MODEM_ICERA (simple), properties); + + parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); + parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!strcmp (subsys, "tty")) { + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_AT_SERIAL_PORT (port)) { + g_object_set (port, + MM_PORT_CARRIER_DETECT, FALSE, + MM_SERIAL_PORT_SEND_DELAY, (guint64) 0, + NULL); + + /* Add Icera-specific handlers */ + mm_modem_icera_register_unsolicted_handlers (MM_MODEM_ICERA (gsm), MM_AT_SERIAL_PORT (port)); + } + + return !!port; +} + +static MMModemIceraPrivate * +get_icera_private (MMModemIcera *icera) +{ + return MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (icera)->icera; +} + +static void +modem_init (MMModem *modem_class) +{ + modem_class->reset = reset; + modem_class->disable = disable; + modem_class->connect = do_connect; + modem_class->get_ip4_config = get_ip4_config; + modem_class->grab_port = grab_port; +} + +static void +modem_icera_init (MMModemIcera *icera) +{ + icera->get_private = get_icera_private; +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->set_band = set_band; + class->get_band = get_band; +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_unlock_retries = get_unlock_retries; +} + +static void +mm_modem_samsung_gsm_init (MMModemSamsungGsm *self) +{ +} + +static void +dispose (GObject *object) +{ + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (object); + MMModemSamsungGsmPrivate *priv = MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (self); + + if (priv->disposed == FALSE) { + priv->disposed = TRUE; + + mm_modem_icera_dispose_private (MM_MODEM_ICERA (self)); + } + + G_OBJECT_CLASS (mm_modem_samsung_gsm_parent_class)->dispose (object); +} + +static void +mm_modem_samsung_gsm_class_init (MMModemSamsungGsmClass *klass) +{ + + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_samsung_gsm_parent_class = g_type_class_peek_parent (klass); + + g_type_class_add_private (object_class, sizeof (MMModemSamsungGsmPrivate)); + + object_class->dispose = dispose; + + gsm_class->do_disconnect = do_disconnect; + gsm_class->do_enable = do_enable; + + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; +} diff --git a/plugins/mm-modem-samsung-gsm.h b/plugins/mm-modem-samsung-gsm.h new file mode 100755 index 0000000..9e50716 --- /dev/null +++ b/plugins/mm-modem-samsung-gsm.h @@ -0,0 +1,47 @@ +/* -*- 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. + * Copyright (C) 2011 Samsung Electronics, Inc., + */ + +#ifndef MM_MODEM_SAMSUNG_GSM_H +#define MM_MODEM_SAMSUNG_GSM_H + +#include + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_SAMSUNG_GSM (mm_modem_samsung_gsm_get_type ()) +#define MM_MODEM_SAMSUNG_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsm)) +#define MM_MODEM_SAMSUNG_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsmClass)) +#define MM_IS_MODEM_SAMSUNG_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SAMSUNG_GSM)) +#define MM_IS_MODEM_SAMSUNG_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SAMSUNG_GSM)) +#define MM_MODEM_SAMSUNG_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemSamsungGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemSamsungGsmClass; + +GType mm_modem_samsung_gsm_get_type (void); + +MMModem *mm_modem_samsung_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_SAMSUNG_GSM_H */ + diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c new file mode 100644 index 0000000..3c8a49b --- /dev/null +++ b/plugins/mm-modem-wavecom-gsm.c @@ -0,0 +1,1099 @@ +/* -*- 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) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#include + +#include +#include +#include +#include + +#include "mm-errors.h" +#include "mm-modem-helpers.h" +#include "mm-modem-wavecom-gsm.h" +#include "mm-serial-parsers.h" +#include "mm-log.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); + +G_DEFINE_TYPE_EXTENDED (MMModemWavecomGsm, mm_modem_wavecom_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)) + +/* Bit flags for mobile station classes supported by the modem */ +typedef enum { + WAVECOM_MS_CLASS_UNKNOWN = 0, + /* Class C in circuit switched only mode, CS */ + WAVECOM_MS_CLASS_CC = 1 << 0, + /* Class C in GPRS only mode, PS */ + WAVECOM_MS_CLASS_CG = 1 << 1, + /* Class B (either CS or PS, not both at the same time) + * This should be the default for GSM/GPRS modems */ + WAVECOM_MS_CLASS_B = 1 << 2, + /* Class A in 3G only mode */ + WAVECOM_MS_CLASS_A = 1 << 3 +} WavecomMSClass; + +#define WAVECOM_MS_CLASS_CC_IDSTR "\"CC\"" +#define WAVECOM_MS_CLASS_CG_IDSTR "\"CG\"" +#define WAVECOM_MS_CLASS_B_IDSTR "\"B\"" +#define WAVECOM_MS_CLASS_A_IDSTR "\"A\"" + +/* Mask of all supported 2G bands */ +#define ALL_2G_BANDS \ + (MM_MODEM_GSM_BAND_EGSM | \ + MM_MODEM_GSM_BAND_DCS | \ + MM_MODEM_GSM_BAND_PCS | \ + MM_MODEM_GSM_BAND_G850) + +/* Mask of all supported 3G bands */ +#define ALL_3G_BANDS \ + (MM_MODEM_GSM_BAND_U2100 | \ + MM_MODEM_GSM_BAND_U1800 | \ + MM_MODEM_GSM_BAND_U17IV | \ + MM_MODEM_GSM_BAND_U800 | \ + MM_MODEM_GSM_BAND_U850 | \ + MM_MODEM_GSM_BAND_U900 | \ + MM_MODEM_GSM_BAND_U17IX | \ + MM_MODEM_GSM_BAND_U1900 | \ + MM_MODEM_GSM_BAND_U2600) + +#define MM_MODEM_WAVECOM_GSM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsmPrivate)) + +typedef struct { + /* Bitmask for supported MS classes */ + guint8 supported_ms_classes; + /* Current MS class */ + WavecomMSClass current_ms_class; + /* Current allowed mode, only for 3G devices */ + MMModemGsmAllowedMode allowed_mode; + /* Bitmask for currently active bands */ + guint32 current_bands; +} MMModemWavecomGsmPrivate; + +/* Setup relationship between 2G bands in the modem (identified by a + * single digit in ASCII) and the bitmask in ModemManager. */ +typedef struct { + gchar wavecom_band; + guint32 mm_band_mask; +} WavecomBand2G; +static const WavecomBand2G bands_2g[] = { + { '0', MM_MODEM_GSM_BAND_G850 }, + { '1', MM_MODEM_GSM_BAND_EGSM }, + { '2', MM_MODEM_GSM_BAND_DCS }, + { '3', MM_MODEM_GSM_BAND_PCS }, + { '4', (MM_MODEM_GSM_BAND_G850 | MM_MODEM_GSM_BAND_PCS) }, + { '5', (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_DCS) }, + { '6', (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_PCS) }, + { '7', ALL_2G_BANDS } +}; + +/* Setup relationship between the 3G band bitmask in the modem and the bitmask + * in ModemManager. */ +typedef struct { + guint32 wavecom_band_flag; + guint32 mm_band_flag; +} WavecomBand3G; +static const WavecomBand3G bands_3g[] = { + { (1 << 0), MM_MODEM_GSM_BAND_U2100 }, + { (1 << 1), MM_MODEM_GSM_BAND_U1900 }, + { (1 << 2), MM_MODEM_GSM_BAND_U1800 }, + { (1 << 3), MM_MODEM_GSM_BAND_U17IV }, + { (1 << 4), MM_MODEM_GSM_BAND_U850 }, + { (1 << 5), MM_MODEM_GSM_BAND_U800 }, + { (1 << 6), MM_MODEM_GSM_BAND_U2600 }, + { (1 << 7), MM_MODEM_GSM_BAND_U900 }, + { (1 << 8), MM_MODEM_GSM_BAND_U17IX } +}; + +MMModem * +mm_modem_wavecom_gsm_new (const char *device, + const char *driver, + const char *plugin, + guint32 vendor, + guint32 product) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_WAVECOM_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, + NULL)); +} + +static const gchar * +wavecom_ms_class_to_str (WavecomMSClass class) +{ + switch (class) { + case WAVECOM_MS_CLASS_CC: + return WAVECOM_MS_CLASS_CC_IDSTR; + case WAVECOM_MS_CLASS_CG: + return WAVECOM_MS_CLASS_CG_IDSTR; + case WAVECOM_MS_CLASS_B: + return WAVECOM_MS_CLASS_B_IDSTR; + case WAVECOM_MS_CLASS_A: + return WAVECOM_MS_CLASS_A_IDSTR; + default: + g_warn_if_reached (); + return NULL; + } +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_AT_SERIAL_PORT (port)) { + gpointer parser; + GRegex *regex; + + parser = mm_serial_parser_v1_new (); + + /* AT+CPIN? replies will never have an OK appended */ + regex = g_regex_new ("\\r\\n\\+CPIN: .*\\r\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, NULL); + mm_serial_parser_v1_set_custom_regex (parser, regex, NULL); + g_regex_unref (regex); + + mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port), + mm_serial_parser_v1_parse, + parser, + mm_serial_parser_v1_destroy); + } + + return !!port; +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + /* Do nothing... see set_property() in parent, which also does nothing */ +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + /* Wavecom doesn't like CFUN=1, it will reset the whole software stack, + * including the USB connection and therefore connection would get + * closed */ + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: + /* Wavecom doesn't have XOFF/XON flow control, so we enable RTS/CTS */ + g_value_set_string (value, "+IFC=2,2"); + break; + default: + break; + } +} + +static void +set_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else + priv->current_bands = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-band")); + + mm_callback_info_schedule (info); +} + +static void +set_2g_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMAtSerialPort *port, + MMCallbackInfo *info) +{ + gchar wavecom_band; + gchar *cmd; + guint i; + + /* Ensure we don't get 3G bands when trying to configure 2G bands */ + if (band & ALL_3G_BANDS) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Not allowed to set 3G bands in 2G mode"); + mm_callback_info_schedule (info); + return; + } + + /* If we get ANY, reset to all-2G bands to get the proper value */ + if (band == MM_MODEM_GSM_BAND_ANY) + band = ALL_2G_BANDS; + + /* Loop looking for allowed masks */ + wavecom_band = '\0'; + for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { + if (bands_2g[i].mm_band_mask == band) { + wavecom_band = bands_2g[i].wavecom_band; + break; + } + } + + /* If we didn't find a match, set an error */ + if (wavecom_band == '\0') { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Wrong 2G band mask: '%u'", band); + mm_callback_info_schedule (info); + return; + } + + mm_callback_info_set_data (info, + "new-band", + GUINT_TO_POINTER ((guint)band), + NULL); + + cmd = g_strdup_printf ("+WMBS=%c,1", wavecom_band); + mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info); + g_free (cmd); +} + +static void +set_3g_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMAtSerialPort *port, + MMCallbackInfo *info) +{ + guint wavecom_band; + gchar *cmd; + guint i; + + /* Ensure we don't get 2G bands when trying to configure 2G bands */ + if (band & ALL_2G_BANDS) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Not allowed to set 2G bands in 3G mode"); + mm_callback_info_schedule (info); + return; + } + + /* If we get ANY, reset to all-3G bands to get the proper value */ + if (band == MM_MODEM_GSM_BAND_ANY) + band = ALL_3G_BANDS; + + /* Loop looking for allowed masks */ + wavecom_band = 0; + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { + if (bands_3g[i].mm_band_flag & band) { + wavecom_band |= bands_3g[i].wavecom_band_flag; + } + } + + /* If we didn't find a match, set an error */ + if (wavecom_band == 0) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Wrong 3G band mask: '%u'", band); + mm_callback_info_schedule (info); + return; + } + + mm_callback_info_set_data (info, + "new-band", + GUINT_TO_POINTER ((guint)band), + NULL); + + cmd = g_strdup_printf ("+WMBS=\"%u\",1", wavecom_band); + mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info); + g_free (cmd); +} + +static void +set_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + + /* Are we trying to change the band to the same bands currently + * being used? if so, we're done */ + if (priv->current_bands == band) { + mm_callback_info_schedule (info); + return; + } + + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) + set_2g_band (self, band, port, info); + else + set_3g_band (self, band, port, info); +} + +static void +get_2g_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else { + const gchar *p; + guint32 mm_band = MM_MODEM_GSM_BAND_UNKNOWN; + + p = mm_strip_tag (response->str, "+WMBS:"); + if (p) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { + if (bands_2g[i].wavecom_band == *p) { + mm_band = bands_2g[i].mm_band_mask; + break; + } + } + } + + if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) { + g_set_error (&info->error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get 2G bands"); + } else { + priv->current_bands = mm_band; + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + } + } + + mm_callback_info_schedule (info); +} + +static void +get_3g_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else { + const gchar *p; + guint mm_band = MM_MODEM_GSM_BAND_UNKNOWN; + guint32 wavecom_band; + + /* Example reply: + * AT+WUBS? --> + * <-- +WUBS: "3",1 + * <-- OK + * The "3" meaning here Band I and II are selected. + */ + + p = mm_strip_tag (response->str, "+WUBS:"); + if (*p == '"') + p++; + wavecom_band = atoi (p); + + if (wavecom_band > 0) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { + if (bands_3g[i].wavecom_band_flag & wavecom_band) { + mm_band |= bands_3g[i].mm_band_flag; + } + } + } + + if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) { + g_set_error (&info->error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get 3G bands"); + } else { + priv->current_bands = mm_band; + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + } + } + + mm_callback_info_schedule (info); +} + +static void +get_band (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); + + /* If results are already cached, return them */ + if (priv->current_bands > 0) { + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->current_bands), NULL); + mm_callback_info_schedule (info); + return; + } + + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) + mm_at_serial_port_queue_command (port, "AT+WMBS?", 3, get_2g_band_done, info); + else + mm_at_serial_port_queue_command (port, "AT+WUBS?", 3, get_3g_band_done, info); +} + +static void +get_access_technology_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const gchar *p; + + if (error) + info->error = g_error_copy (error); + else { + p = mm_strip_tag (response->str, "+WGPRSIND:"); + if (!p) { + g_set_error (&info->error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get network capabilities"); + } else { + switch (*p) { + case '1': + /* GPRS only */ + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + break; + case '2': + /* EGPRS/EDGE supported */ + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + break; + case '3': + /* 3G R99 supported */ + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + break; + case '4': + /* HSDPA supported */ + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + break; + case '5': + /* HSUPA supported */ + act = MM_MODEM_GSM_ACCESS_TECH_HSUPA; + break; + default: + break; + } + } + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+WGPRS=9,2", 3, get_access_technology_cb, info); +} + +static void +get_allowed_mode_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + gint read_mode = -1; + gchar *mode_str = NULL; + gchar *prefer_str = NULL; + GRegex *r = NULL; + GMatchInfo *match_info = NULL; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + return; + } + + /* Possible responses: + * +WWSM: 0 (2G only) + * +WWSM: 1 (3G only) + * +WWSM: 2,0 (Any) + * +WWSM: 2,1 (2G preferred) + * +WWSM: 2,2 (3G preferred) + */ + r = g_regex_new ("\\r\\n\\+WWSM: ([0-2])(,([0-2]))?.*$", 0, 0, NULL); + if (r && g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, NULL)) { + mode_str = g_match_info_fetch (match_info, 1); + prefer_str = g_match_info_fetch (match_info, 3); /* 3, to avoid the comma */ + + if (mode_str) { + switch (atoi (mode_str)) { + case 0: + if (!prefer_str) + read_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + break; + case 1: + if (!prefer_str) + read_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + break; + case 2: + if (prefer_str) { + switch (atoi (prefer_str)) { + case 0: + read_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + break; + case 1: + read_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; + break; + case 2: + read_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + break; + default: + g_warn_if_reached (); + break; + } + } + break; + default: + g_warn_if_reached (); + break; + } + } + } + + if (read_mode < 0) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unexpected wireless data service reply: '%s' " + "(mode: '%s', prefer: '%s')", + response->str, + mode_str ? mode_str : "none", + prefer_str ? prefer_str : "none"); + } else { + priv->allowed_mode = (guint)read_mode; + mm_callback_info_set_result (info, + GUINT_TO_POINTER (priv->allowed_mode), + NULL); + } + + if (r) + g_regex_unref (r); + if (match_info) + g_match_info_free (match_info); + g_free (mode_str); + g_free (prefer_str); + + mm_callback_info_schedule (info); +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (gsm); + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + /* For 3G devices, query WWSM status */ + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + MMAtSerialPort *port; + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+WWSM?", 3, get_allowed_mode_cb, info); + return; + } + + /* For 2G devices, just return cached value */ + mm_callback_info_set_result (info, + GUINT_TO_POINTER (priv->allowed_mode), + NULL); + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else + priv->allowed_mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-mode")); + + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (gsm); + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + /* For 3G devices, go on with WWSM */ + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + MMAtSerialPort *port; + GString *cmd; + gint net = -1; + gint prefer = -1; + + /* Get port */ + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_callback_info_set_data (info, + "new-mode", + GUINT_TO_POINTER (mode), + NULL); + + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + net = 2; + prefer = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + net = 2; + prefer = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + net = 2; + prefer = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + net = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + net = 1; + break; + } + + cmd = g_string_new ("+WWSM="); + g_string_append_printf (cmd, "%d", net); + if (net == 2) + g_string_append_printf (cmd, ",%d", prefer); + mm_at_serial_port_queue_command (port, cmd->str, 3, set_allowed_mode_cb, info); + g_string_free (cmd, TRUE); + + return; + } + + /* For non-3G devices, allow only 2G-related allowed modes */ + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + priv->allowed_mode = mode; + break; + default: + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Cannot set desired allowed mode, " + "not a 3G device"); + break; + } + + mm_callback_info_schedule (info); +} + +static void +enable_complete (MMGenericGsm *gsm, + GError *error, + MMCallbackInfo *info) +{ + /* Do NOT chain up parent do_enable_power_up_done(), as it actually ignores + * all errors. */ + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); +} + +static void +set_highest_ms_class_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + guint new_class; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + new_class = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-class")); + if (new_class) + priv->current_ms_class = new_class; + + /* All done without errors! */ + mm_dbg ("[5/5] All done"); + enable_complete (MM_GENERIC_GSM (info->modem), NULL, info); +} + +static void +set_highest_ms_class (MMAtSerialPort *port, + MMCallbackInfo *info) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + guint new_class = 0; + + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) { + /* A is supported but is not currently selected, switch to A */ + new_class = WAVECOM_MS_CLASS_A; + } + } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_B) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_B) { + /* B is supported but is not currently selected, switch to B */ + new_class = WAVECOM_MS_CLASS_B; + } + } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_CG) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_CG) { + /* CG is supported but is not currently selected, switch to CG */ + new_class = WAVECOM_MS_CLASS_CG; + } + } + + if (new_class) { + const gchar *new_class_str; + gchar *cmd; + + new_class_str = wavecom_ms_class_to_str (new_class); + mm_dbg ("Changing mobile station class to: %s", new_class_str); + mm_callback_info_set_data (info, + "new-class", + GUINT_TO_POINTER (new_class), + NULL); + cmd = g_strdup_printf ("+CGCLASS=%s", new_class_str); + mm_at_serial_port_queue_command (port, cmd, 3, set_highest_ms_class_cb, info); + g_free (cmd); + return; + } + + /* if no need to change station class, then just go on */ + mm_dbg ("No need to change mobile station class"); + set_highest_ms_class_cb (port, NULL, NULL, info); +} + +static void +get_current_ms_class_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + const gchar *p; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + p = mm_strip_tag (response->str, "+CGCLASS:"); + + if (strncmp (p, + WAVECOM_MS_CLASS_A_IDSTR, + strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class A mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_A; + } else if (strncmp (p, + WAVECOM_MS_CLASS_B_IDSTR, + strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class B mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_B; + } else if (strncmp (p, + WAVECOM_MS_CLASS_CG_IDSTR, + strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class CG mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_CG; + } else if (strncmp (p, + WAVECOM_MS_CLASS_CC_IDSTR, + strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class CC mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_CC; + } else { + GError *inner_error; + + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown mobile station class: '%s'", + p); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + return; + } + + /* Next, set highest mobile station class possible */ + mm_dbg ("[4/5] Ensuring highest MS class..."); + set_highest_ms_class (port, info); +} + +static void +get_supported_ms_classes_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + const gchar *p; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + /* Reset currently supported MS classes */ + priv->supported_ms_classes = 0; + + p = mm_strip_tag (response->str, "+CGCLASS:"); + + if (strstr (p, WAVECOM_MS_CLASS_A_IDSTR)) { + mm_dbg ("Modem supports Class A mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_A; + } + + if (strstr (p, WAVECOM_MS_CLASS_B_IDSTR)) { + mm_dbg ("Modem supports Class B mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_B; + } + + if (strstr (p, WAVECOM_MS_CLASS_CG_IDSTR)) { + mm_dbg ("Modem supports Class CG mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_CG; + } + + if (strstr (p, WAVECOM_MS_CLASS_CC_IDSTR)) { + mm_dbg ("Modem supports Class CC mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_CC; + } + + /* If none received, error */ + if (!priv->supported_ms_classes) { + GError *inner_error; + + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get supported mobile station classes"); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + return; + } + + /* Next, query for current MS class */ + mm_dbg ("[3/5] Getting current MS class..."); + mm_at_serial_port_queue_command (port, "+CGCLASS?", 3, get_current_ms_class_cb, info); +} + +static void +get_current_functionality_status_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + const gchar *p; + GError *inner_error; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + p = mm_strip_tag (response->str, "+CFUN:"); + if (!p || *p != '1') { + /* Reported functionality status MUST be '1'. Otherwise, RF is probably + * switched off. */ + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unexpected functionality status: '%c'. ", + p ? *p :' '); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + } + + /* Nex, query for supported MS classes */ + mm_dbg ("[2/5] Getting supported MS classes..."); + mm_at_serial_port_queue_command (port, "+CGCLASS=?", 3, get_supported_ms_classes_cb, info); +} + +static void +do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + MMAtSerialPort *port; + GError *inner_error = NULL; + + if (error) { + enable_complete (gsm, error, info); + return; + } + + /* Get port */ + port = mm_generic_gsm_get_best_at_port (gsm, &inner_error); + if (!port) { + enable_complete (gsm, inner_error, info); + g_error_free (inner_error); + return; + } + + /* Next, get current functionality status */ + mm_dbg ("[1/5] Getting current functionality status..."); + mm_at_serial_port_queue_command (port, "+CFUN?", 3, get_current_functionality_status_cb, info); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *network_class) +{ + network_class->set_band = set_band; + network_class->get_band = get_band; +} + +static void +mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + + /* Set defaults */ + priv->supported_ms_classes = 0; /* This is a bitmask, so empty */ + priv->current_ms_class = WAVECOM_MS_CLASS_UNKNOWN; + priv->allowed_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + priv->current_bands = 0; /* This is a bitmask, so empty */ +} + +static void +mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMModemWavecomGsmPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_POWER_UP_CMD); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD, + MM_GENERIC_GSM_FLOW_CONTROL_CMD); + + gsm_class->do_enable_power_up_done = do_enable_power_up_done; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; +} + diff --git a/plugins/mm-modem-wavecom-gsm.h b/plugins/mm-modem-wavecom-gsm.h new file mode 100644 index 0000000..f8289e3 --- /dev/null +++ b/plugins/mm-modem-wavecom-gsm.h @@ -0,0 +1,44 @@ +/* -*- 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) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#ifndef MM_MODEM_WAVECOM_GSM_H +#define MM_MODEM_WAVECOM_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_WAVECOM_GSM (mm_modem_wavecom_gsm_get_type ()) +#define MM_MODEM_WAVECOM_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsm)) +#define MM_MODEM_WAVECOM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM, MMModemWavecomGsmClass)) +#define MM_IS_MODEM_WAVECOM_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM)) +#define MM_IS_MODEM_WAVECOM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM)) +#define MM_MODEM_WAVECOM_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM, MMModemWavecomGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemWavecomGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemWavecomGsmClass; + +GType mm_modem_wavecom_gsm_get_type (void); + +MMModem *mm_modem_wavecom_gsm_new (const char *device, + const char *driver, + const char *plugin_name, + guint32 vendor, + guint32 product); + +#endif /* MM_MODEM_WAVECOM_GSM_H */ diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index c447c58..e7387a0 100644 --- a/plugins/mm-modem-zte.c +++ b/plugins/mm-modem-zte.c @@ -39,10 +39,12 @@ G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, #define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate)) typedef struct { + gboolean disposed; gboolean init_retried; guint32 cpms_tries; guint cpms_timeout; gboolean is_icera; + MMModemIceraPrivate *icera; } MMModemZtePrivate; MMModem * @@ -52,17 +54,23 @@ mm_modem_zte_new (const char *device, guint32 vendor, guint32 product) { + MMModem *modem; + g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); g_return_val_if_fail (plugin != NULL, NULL); - return MM_MODEM (g_object_new (MM_TYPE_MODEM_ZTE, - MM_MODEM_MASTER_DEVICE, device, - MM_MODEM_DRIVER, driver, - MM_MODEM_PLUGIN, plugin, - MM_MODEM_HW_VID, vendor, - MM_MODEM_HW_PID, product, - NULL)); + modem = MM_MODEM (g_object_new (MM_TYPE_MODEM_ZTE, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, + NULL)); + if (modem) + MM_MODEM_ZTE_GET_PRIVATE (modem)->icera = mm_modem_icera_init_private (); + + return modem; } /*****************************************************************************/ @@ -547,12 +555,13 @@ simple_connect (MMModemSimple *simple, MMModemFn callback, gpointer user_data) { - MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (simple); + MMModemZte *self = MM_MODEM_ZTE (simple); + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemSimple *parent_iface; if (priv->is_icera) - mm_modem_icera_simple_connect (MM_MODEM_ICERA (simple), properties); + mm_modem_icera_simple_connect (MM_MODEM_ICERA (self), properties); parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); @@ -619,6 +628,14 @@ grab_port (MMModem *modem, /*****************************************************************************/ +static MMModemIceraPrivate * +get_icera_private (MMModemIcera *icera) +{ + return MM_MODEM_ZTE_GET_PRIVATE (icera)->icera; +} + +/*****************************************************************************/ + static void modem_init (MMModem *modem_class) { @@ -629,9 +646,9 @@ modem_init (MMModem *modem_class) } static void -modem_icera_init (MMModemIcera *icera_class) +modem_icera_init (MMModemIcera *icera) { - mm_modem_icera_prepare (icera_class); + icera->get_private = get_icera_private; } static void @@ -651,10 +668,14 @@ dispose (GObject *object) MMModemZte *self = MM_MODEM_ZTE (object); MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); - if (priv->cpms_timeout) - g_source_remove (priv->cpms_timeout); + if (priv->disposed == FALSE) { + priv->disposed = TRUE; - mm_modem_icera_cleanup (MM_MODEM_ICERA (self)); + if (priv->cpms_timeout) + g_source_remove (priv->cpms_timeout); + + mm_modem_icera_dispose_private (MM_MODEM_ICERA (self)); + } G_OBJECT_CLASS (mm_modem_zte_parent_class)->dispose (object); } diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c index 76df673..d4c7e6d 100644 --- a/plugins/mm-plugin-anydata.c +++ b/plugins/mm-plugin-anydata.c @@ -94,7 +94,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c index a27704b..89912c2 100644 --- a/plugins/mm-plugin-generic.c +++ b/plugins/mm-plugin-generic.c @@ -97,7 +97,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c index 36771d1..06f5585 100644 --- a/plugins/mm-plugin-gobi.c +++ b/plugins/mm-plugin-gobi.c @@ -90,7 +90,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c index 7b71932..a095802 100644 --- a/plugins/mm-plugin-hso.c +++ b/plugins/mm-plugin-hso.c @@ -88,7 +88,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 0, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c index f1590ca..32edfde 100644 --- a/plugins/mm-plugin-huawei.c +++ b/plugins/mm-plugin-huawei.c @@ -15,6 +15,7 @@ */ #include +#include #include #define G_UDEV_API_IS_SUBJECT_TO_CHANGE @@ -28,6 +29,7 @@ #include "mm-serial-parsers.h" #include "mm-at-serial-port.h" #include "mm-log.h" +#include "mm-errors.h" G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE) @@ -44,6 +46,8 @@ mm_plugin_create (void) /*****************************************************************************/ +#define TAG_HUAWEI_PCUI_PORT "huawei-pcui-port" + #define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ MM_PLUGIN_BASE_PORT_CAP_IS856 | \ @@ -70,95 +74,59 @@ probe_result (MMPluginBase *base, mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); } -#define TAG_SUPPORTS_INFO "huawei-supports-info" - -typedef struct { - MMAtSerialPort *serial; - guint id; - MMPortType ptype; - /* Whether or not there's already a detected modem that "owns" this port, - * in which case we'll claim it, but if no capabilities are detected it'll - * just be ignored. - */ - gboolean parent_modem; -} HuaweiSupportsInfo; - -static void -huawei_supports_info_destroy (gpointer user_data) -{ - HuaweiSupportsInfo *info = user_data; - - if (info->id) - g_source_remove (info->id); - if (info->serial) - g_object_unref (info->serial); - memset (info, 0, sizeof (HuaweiSupportsInfo)); - g_free (info); -} - static gboolean -probe_secondary_supported (gpointer user_data) +getportmode_response_cb (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data) { - MMPluginBaseSupportsTask *task = user_data; - HuaweiSupportsInfo *info; - - info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - - info->id = 0; - g_object_unref (info->serial); - info->serial = NULL; + /* If any error occurred that was not ERROR or COMMAND NOT SUPPORT then + * retry the command. + */ + if (error) { + if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_UNKNOWN) == FALSE) + return tries <= 4 ? TRUE : FALSE; + } else { + MMPlugin *plugin; + char *p; + int i = 0; + + /* Get the USB interface number of the PCUI port */ + p = strstr (response->str, "PCUI:"); + if (p) + i = atoi (p + strlen ("PCUI:")); + + if (i) { + /* Save they PCUI port number for later */ + plugin = mm_plugin_base_supports_task_get_plugin (task); + g_assert (plugin); + g_object_set_data (G_OBJECT (plugin), TAG_HUAWEI_PCUI_PORT, GINT_TO_POINTER (i)); + } + } - /* Yay, supported, we got an unsolicited message */ - info->ptype = MM_PORT_TYPE_SECONDARY; - mm_plugin_base_supports_task_complete (task, 10); + /* No error or if ^GETPORTMODE is not supported, assume success */ return FALSE; } -static void -probe_secondary_handle_msg (MMAtSerialPort *port, - GMatchInfo *match_info, - gpointer user_data) -{ - MMPluginBaseSupportsTask *task = user_data; - HuaweiSupportsInfo *info; - - info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - g_source_remove (info->id); - info->id = g_idle_add (probe_secondary_supported, task); -} - static gboolean -probe_secondary_timeout (gpointer user_data) +curc_response_cb (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data) { - MMPluginBaseSupportsTask *task = user_data; - HuaweiSupportsInfo *info; - guint level = 0; - - info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - info->id = 0; - g_object_unref (info->serial); - info->serial = NULL; - - /* Supported, but ignored if this port's parent device is already a modem */ - if (info->parent_modem) { - info->ptype = MM_PORT_TYPE_IGNORED; - level = 10; - } + if (error) + return tries <= 4 ? TRUE : FALSE; - mm_plugin_base_supports_task_complete (task, level); + /* No error, assume success */ return FALSE; } -static void -add_regex (MMAtSerialPort *port, const char *match, gpointer user_data) -{ - GRegex *regex; - - regex = g_regex_new (match, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_at_serial_port_add_unsolicited_msg_handler (port, regex, probe_secondary_handle_msg, user_data, NULL); - g_regex_unref (regex); -} - static MMPluginSupportsResult supports_port (MMPluginBase *base, MMModem *existing, @@ -169,7 +137,6 @@ supports_port (MMPluginBase *base, const char *subsys, *name; int usbif; guint16 vendor = 0, product = 0; - guint32 existing_type = MM_MODEM_TYPE_UNKNOWN; /* Can't do anything with non-serial ports */ port = mm_plugin_base_supports_task_get_port (task); @@ -189,72 +156,48 @@ supports_port (MMPluginBase *base, if (usbif < 0) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; - /* The secondary ports don't necessarily respond correctly to probing, so - * we need to use the first port that does respond to probing to create the - * right type of mode (GSM or CDMA), and then re-check the other interfaces. + /* The primary port (called the "modem" port in the Windows drivers) is + * always USB interface 0, and we need to detect that interface first for + * two reasons: (1) to disable unsolicited messages on other ports that + * may fill up the buffer and crash the device, and (2) to attempt to get + * the port layout for hints about what the secondary port is (called the + * "pcui" port in Windows). Thus we probe USB interface 0 first and defer + * probing other interfaces until we've got if0, at which point we allow + * the other ports to be probed too. */ if (!existing && usbif != 0) return MM_PLUGIN_SUPPORTS_PORT_DEFER; - /* CDMA devices don't have problems with the secondary ports, so after - * ensuring we have a device by probing the first port, probe the secondary - * ports on CDMA devices too. - */ - if (existing) - g_object_get (G_OBJECT (existing), MM_MODEM_TYPE, &existing_type, NULL); - - if (usbif == 0 || (existing_type == MM_MODEM_TYPE_CDMA)) { - if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { - level = get_level_for_capabilities (cached); - if (level) { - mm_plugin_base_supports_task_complete (task, level); - return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; - } - return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; - } - - /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; - } else { - HuaweiSupportsInfo *info; - GError *error = NULL; - - /* Listen for Huawei-specific unsolicited messages */ - info = g_malloc0 (sizeof (HuaweiSupportsInfo)); - info->parent_modem = !!existing; - - info->serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY); - g_object_set (G_OBJECT (info->serial), MM_PORT_CARRIER_DETECT, FALSE, NULL); - - mm_at_serial_port_set_response_parser (info->serial, - mm_serial_parser_v1_parse, - mm_serial_parser_v1_new (), - mm_serial_parser_v1_destroy); - - add_regex (info->serial, "\\r\\n\\^RSSI:(\\d+)\\r\\n", task); - add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task); - add_regex (info->serial, "\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", task); - add_regex (info->serial, "\\r\\n\\^BOOT:.+\\r\\n", task); - add_regex (info->serial, "\\r\\r\\^BOOT:.+\\r\\r", task); - - info->id = g_timeout_add_seconds (7, probe_secondary_timeout, task); - - if (!mm_serial_port_open (MM_SERIAL_PORT (info->serial), &error)) { - mm_warn ("(Huawei) %s: couldn't open serial port: (%d) %s", - name, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - huawei_supports_info_destroy (info); - return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } - g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO, - info, huawei_supports_info_destroy); + /* Turn off unsolicited messages on secondary ports until needed, + * and try to get a port map from the modem. The response will + * get handled in custom_init_response(). + */ + if (usbif == 0) { + mm_plugin_base_supports_task_add_custom_init_command (task, + "AT^CURC=0", + 3, /* delay */ + curc_response_cb, + NULL); + + mm_plugin_base_supports_task_add_custom_init_command (task, + "AT^GETPORTMODE", + 3, /* delay */ + getportmode_response_cb, + NULL); + } + /* Kick off a probe */ + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; - } return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } @@ -314,12 +257,21 @@ grab_port (MMPluginBase *base, } } } else { - HuaweiSupportsInfo *info; MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + int pcui_usbif, port_usbif; + + /* Any additional AT ports can be secondary ports, but we want to ensure + * that the "pcui" port found from ^GETPORTMODE above is always set as + * a secondary port too. + */ + + port_usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM"); + pcui_usbif = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (base), TAG_HUAWEI_PCUI_PORT)); - info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - if (info) - ptype = info->ptype; + if ( (port_usbif == pcui_usbif) + || (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) + || (caps & CAP_CDMA)) + ptype = MM_PORT_TYPE_SECONDARY; else if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) ptype = MM_PORT_TYPE_QCDM; diff --git a/plugins/mm-plugin-linktop.c b/plugins/mm-plugin-linktop.c index 411f9cc..8d2e199 100644 --- a/plugins/mm-plugin-linktop.c +++ b/plugins/mm-plugin-linktop.c @@ -85,7 +85,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c index e63ff4e..cbf4c1e 100644 --- a/plugins/mm-plugin-longcheer.c +++ b/plugins/mm-plugin-longcheer.c @@ -97,7 +97,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c index 6684ff9..d770ad4 100644 --- a/plugins/mm-plugin-mbm.c +++ b/plugins/mm-plugin-mbm.c @@ -116,7 +116,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 0, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c index 21b737e..f308b9d 100644 --- a/plugins/mm-plugin-moto-c.c +++ b/plugins/mm-plugin-moto-c.c @@ -90,7 +90,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c index 938f8c5..28e9022 100644 --- a/plugins/mm-plugin-nokia.c +++ b/plugins/mm-plugin-nokia.c @@ -93,7 +93,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c index 5384456..d960add 100644 --- a/plugins/mm-plugin-novatel.c +++ b/plugins/mm-plugin-novatel.c @@ -99,7 +99,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c index ab2bd3d..a819b4e 100644 --- a/plugins/mm-plugin-option.c +++ b/plugins/mm-plugin-option.c @@ -89,7 +89,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 0, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-samsung.c b/plugins/mm-plugin-samsung.c new file mode 100755 index 0000000..eb5e071 --- /dev/null +++ b/plugins/mm-plugin-samsung.c @@ -0,0 +1,157 @@ +/* -*- 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 - 2011 Red Hat, Inc. + * Copyright (c) 2011 Samsung Electronics, Inc., + */ + +#include +#include +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include + +#include "mm-plugin-samsung.h" +#include "mm-modem-samsung-gsm.h" + +G_DEFINE_TYPE (MMPluginSamsung, mm_plugin_samsung, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_SAMSUNG, + MM_PLUGIN_BASE_NAME, "Samsung", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + const char *subsys, *name; + guint16 vendor = 0, product = 0; + + port = mm_plugin_base_supports_task_get_port (task); + + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + name = g_udev_device_get_name (port); + g_assert (name); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* Vendor ID check */ + if (vendor != 0x04e8 && vendor != 0x1983) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* The ethernet ports are obviously supported and don't need probing */ + if (!strcmp (subsys, "net")) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, 0, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL; + MMModem *modem = NULL; + guint32 caps; + const char *name, *subsys, *sysfs_path; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (caps & CAP_CDMA) { + g_set_error (error, 0, 0, "Only GSM modems are currently supported by this plugin."); + return NULL; + } + + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + modem = mm_modem_samsung_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +static void +mm_plugin_samsung_init (MMPluginSamsung *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_samsung_class_init (MMPluginSamsungClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} + diff --git a/plugins/mm-plugin-samsung.h b/plugins/mm-plugin-samsung.h new file mode 100755 index 0000000..e79daa2 --- /dev/null +++ b/plugins/mm-plugin-samsung.h @@ -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) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + * Copyright 2011 by Samsung Electronics, Inc., + */ + +#ifndef MM_PLUGIN_SAMSUNG_H +#define MM_PLUGIN_SAMSUNG_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_SAMSUNG (mm_plugin_samsung_get_type ()) +#define MM_PLUGIN_SAMSUNG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SAMSUNG, MMPluginSamsung)) +#define MM_PLUGIN_SAMSUNG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SAMSUNG, MMPluginSamsungClass)) +#define MM_IS_PLUGIN_SAMSUNG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SAMSUNG)) +#define MM_IS_PLUGIN_SAMSUNG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SAMSUNG)) +#define MM_PLUGIN_SAMSUNG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SAMSUNG, MMPluginSamsungClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginSamsung; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginSamsungClass; + +GType mm_plugin_samsung_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_SAMSUNG_H */ + diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c index b7367ab..408074b 100644 --- a/plugins/mm-plugin-sierra.c +++ b/plugins/mm-plugin-sierra.c @@ -122,7 +122,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-simtech.c b/plugins/mm-plugin-simtech.c index 611d2d2..7e2e634 100644 --- a/plugins/mm-plugin-simtech.c +++ b/plugins/mm-plugin-simtech.c @@ -97,7 +97,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-wavecom.c b/plugins/mm-plugin-wavecom.c new file mode 100644 index 0000000..c7e3f4a --- /dev/null +++ b/plugins/mm-plugin-wavecom.c @@ -0,0 +1,162 @@ +/* -*- 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. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#include +#include +#include "mm-plugin-wavecom.h" +#include "mm-modem-wavecom-gsm.h" + +G_DEFINE_TYPE (MMPluginWavecom, mm_plugin_wavecom, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_WAVECOM, + MM_PLUGIN_BASE_NAME, "Wavecom", + NULL)); +} + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* Vendor: Wavecom */ + if (vendor != 0x114f) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + guint16 vendor = 0, product = 0; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_wavecom_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else if (get_level_for_capabilities (caps)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_wavecom_init (MMPluginWavecom *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_wavecom_class_init (MMPluginWavecomClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-wavecom.h b/plugins/mm-plugin-wavecom.h new file mode 100644 index 0000000..d01bf15 --- /dev/null +++ b/plugins/mm-plugin-wavecom.h @@ -0,0 +1,47 @@ +/* -*- 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. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#ifndef MM_PLUGIN_WAVECOM_H +#define MM_PLUGIN_WAVECOM_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_WAVECOM (mm_plugin_wavecom_get_type ()) +#define MM_PLUGIN_WAVECOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecom)) +#define MM_PLUGIN_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecomClass)) +#define MM_IS_PLUGIN_WAVECOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_WAVECOM)) +#define MM_IS_PLUGIN_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_WAVECOM)) +#define MM_PLUGIN_WAVECOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecomClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginWavecom; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginWavecomClass; + +GType mm_plugin_wavecom_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_WAVECOM_H */ diff --git a/plugins/mm-plugin-x22x.c b/plugins/mm-plugin-x22x.c index 5a8253f..f99b9d6 100644 --- a/plugins/mm-plugin-x22x.c +++ b/plugins/mm-plugin-x22x.c @@ -98,7 +98,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c index bb2ee17..37033e0 100644 --- a/plugins/mm-plugin-zte.c +++ b/plugins/mm-plugin-zte.c @@ -61,6 +61,22 @@ probe_result (MMPluginBase *base, mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); } +static gboolean +custom_init_response_cb (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data) +{ + if (error) + return tries <= 4 ? TRUE : FALSE; + + /* No error, assume success */ + return FALSE; +} + static MMPluginSupportsResult supports_port (MMPluginBase *base, MMModem *existing, @@ -113,9 +129,13 @@ supports_port (MMPluginBase *base, * 1235f71b20c92cded4abd976ccc5010649aae1a0 and * f38ad328acfdc6ce29dd1380602c546b064161ae for more details. */ - mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, FALSE); + mm_plugin_base_supports_task_add_custom_init_command (task, + "ATE0+CPMS?", + 3, + custom_init_response_cb, + NULL); - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/policy/org.freedesktop.modem-manager.policy.in b/policy/org.freedesktop.modem-manager.policy.in index ed98f63..3790918 100644 --- a/policy/org.freedesktop.modem-manager.policy.in +++ b/policy/org.freedesktop.modem-manager.policy.in @@ -23,7 +23,7 @@ <_message>System policy prevents requesting identifying information from the mobile broadband device. no - auth_self_keep + yes diff --git a/src/mm-charsets.c b/src/mm-charsets.c index d2b9a66..cbdf388 100644 --- a/src/mm-charsets.c +++ b/src/mm-charsets.c @@ -427,17 +427,16 @@ mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len) guint8 * gsm_unpack (const guint8 *gsm, - guint32 gsm_len, + guint32 num_septets, guint8 start_offset, /* in _bits_ */ guint32 *out_unpacked_len) { GByteArray *unpacked; - int i, nchars; + int i; - nchars = ((gsm_len * 8) - start_offset) / 7; - unpacked = g_byte_array_sized_new (nchars + 1); + unpacked = g_byte_array_sized_new (num_septets + 1); - for (i = 0; i < nchars; i++) { + for (i = 0; i < num_septets; i++) { guint8 bits_here, bits_in_next, octet, offset, c; guint32 start_bit; diff --git a/src/mm-charsets.h b/src/mm-charsets.h index 661052d..50b0cce 100644 --- a/src/mm-charsets.h +++ b/src/mm-charsets.h @@ -53,7 +53,7 @@ guint8 *mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len); guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len); guint8 *gsm_unpack (const guint8 *gsm, - guint32 gsm_len, + guint32 num_septets, guint8 start_offset, /* in bits */ guint32 *out_unpacked_len); diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 98713b0..9cb9690 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -167,6 +167,16 @@ static void ciev_received (MMAtSerialPort *port, GMatchInfo *info, gpointer user_data); +static void cmti_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data); + +#define GS_HASH_TAG "get-sms" +static GValue *simple_string_value (const char *str); +static GValue *simple_uint_value (guint32 i); +static GValue *simple_boolean_value (gboolean b); +static void simple_free_gvalue (gpointer data); + MMModem * mm_generic_gsm_new (const char *device, const char *driver, @@ -697,9 +707,10 @@ initial_pin_check (MMGenericGsm *self) g_return_if_fail (priv->primary != NULL); - if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) + if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) { + mm_at_serial_port_queue_command (priv->primary, "+CMEE=1", 2, NULL, NULL); check_pin (self, initial_pin_check_done, NULL); - else { + } else { g_warning ("%s: failed to open serial port: (%d) %s", __func__, error ? error->code : -1, @@ -814,6 +825,9 @@ mm_generic_gsm_grab_port (MMGenericGsm *self, regex = g_regex_new ("\\r\\n\\+CIEV: (\\d+),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, ciev_received, self, NULL); + + regex = g_regex_new ("\\r\\n\\+CMTI: \"(\\S+)\",(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, cmti_received, self, NULL); g_regex_unref (regex); if (ptype == MM_PORT_TYPE_PRIMARY) { @@ -1292,6 +1306,29 @@ ciev_received (MMAtSerialPort *port, /* FIXME: handle roaming and service indicators */ } +static void +cmti_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (user_data); + + guint idx=0; + char *str; + + str = g_match_info_fetch (info, 2); + if (str) + idx = atoi (str); + g_free (str); + + /* todo: parse pdu to know if the sms is complete */ + mm_modem_gsm_sms_received (MM_MODEM_GSM_SMS (self), + idx, + TRUE); + + /* todo: send mm_modem_gsm_sms_completed if complete */ +} + static void cmer_cb (MMAtSerialPort *port, GString *response, @@ -1352,6 +1389,7 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, MMCallbackInfo *info) { MMGenericGsmPrivate *priv; + gchar *cmd = NULL; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_GENERIC_GSM (self)); @@ -1377,8 +1415,16 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, } } - /* Try to enable XON/XOFF flow control */ - mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL); + /* Try to enable flow control */ + g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_FLOW_CONTROL_CMD, &cmd, NULL); + if (cmd && strlen (cmd)) + mm_at_serial_port_queue_command (priv->primary, cmd, 3, NULL, NULL); + g_free (cmd); + + /* Enable SMS notifications */ + mm_at_serial_port_queue_command (priv->primary, "+CNMI=2,1,2,1,0", 3, NULL, NULL); + /* Set SMS storage location to ME */ + mm_at_serial_port_queue_command (priv->primary, "+CPMS=\"ME\",\"ME\",\"ME\"", 3, NULL, NULL); mm_at_serial_port_queue_command (priv->primary, "+CIND=?", 3, cind_cb, self); @@ -1912,8 +1958,17 @@ pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data) /* If we have a saved error from sending PIN/PUK, return that to callers */ saved_error = mm_callback_info_get_data (info, SAVED_ERROR_TAG); if (saved_error) { - g_clear_error (&info->error); - info->error = saved_error; + if (info->modem && !mm_modem_base_get_unlock_required (MM_MODEM_BASE (info->modem))) { + /* Original unlock failed but the modem is actually unlocked, so + * return success. Sometimes happens if the modem doesn't allow + * CPIN="xxxx" when it's already unlocked and returns an error. + * Do nothing. + */ + } else { + /* Unlock failed after recheck, return original error */ + g_clear_error (&info->error); + info->error = g_error_copy (saved_error); + } } mm_callback_info_schedule (info); @@ -1938,7 +1993,8 @@ send_puk_done (MMAtSerialPort *port, * when we're done rechecking CPIN status. */ mm_callback_info_set_data (info, SAVED_ERROR_TAG, - g_error_copy (error), NULL); + g_error_copy (error), + (GDestroyNotify) g_error_free); } } @@ -2001,7 +2057,8 @@ send_pin_done (MMAtSerialPort *port, * when we're done rechecking CPIN status. */ mm_callback_info_set_data (info, SAVED_ERROR_TAG, - g_error_copy (error), NULL); + g_error_copy (error), + (GDestroyNotify) g_error_free); } } @@ -3735,24 +3792,6 @@ set_charset_done (MMAtSerialPort *port, 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, @@ -3767,7 +3806,7 @@ set_charset (MMModem *modem, info = mm_callback_info_new (modem, callback, user_data); - if (!(priv->charsets & charset) || !check_for_single_value (charset)) { + if (!(priv->charsets & charset) || !utils_check_for_single_value (charset)) { info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_UNSUPPORTED_CHARSET, "Character set 0x%X not supported", @@ -3811,6 +3850,29 @@ mm_generic_gsm_get_charset (MMGenericGsm *self) /*****************************************************************************/ /* MMModemGsmSms interface */ + +#define SMS_TP_MTI_MASK 0x03 +#define SMS_TP_MTI_SMS_DELIVER 0x00 +#define SMS_TP_MTI_SMS_SUBMIT_REPORT 0x01 +#define SMS_TP_MTI_SMS_STATUS_REPORT 0x02 + +#define SMS_TP_MMS 0x04 +#define SMS_TP_SRI 0x20 +#define SMS_TP_UDHI 0x40 +#define SMS_TP_RP 0x80 + +#define SMS_DCS_CODING_MASK 0xec +#define SMS_DCS_CODING_DEFAULT 0x00 +#define SMS_DCS_CODING_8BIT 0x04 +#define SMS_DCS_CODING_UCS2 0x08 + +#define SMS_DCS_CLASS_VALID 0x10 +#define SMS_DCS_CLASS_MASK 0x03 + +#define SMS_TIMESTAMP_LEN 7 +#define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN) +#define SMS_MAX_PDU_LEN 344 + static void sms_send_done (MMAtSerialPort *port, GString *response, @@ -3855,6 +3917,414 @@ sms_send (MMModemGsmSms *modem, g_free (command); } +static char sms_bcd_chars[] = "0123456789*#abc\0\0"; + +static void +sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets) +{ + int i; + + for (i = 0 ; i < num_octets; i++) { + *dest++ = sms_bcd_chars[octets[i] & 0xf]; + *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf]; + } + *dest++ = '\0'; +} + +/* len is in septets for gsm7 and in digits (semi-octets) for others */ +static char * +sms_decode_address (const guint8 *address, int len) +{ + guint8 addrtype; + char *utf8; + + addrtype = address[0]; + address++; + + if (addrtype == 0xd0) { + guint8 *unpacked; + guint32 unpacked_len; + unpacked = gsm_unpack (address + 1, len, 0, &unpacked_len); + utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked, + unpacked_len); + g_free(unpacked); + } else { + utf8 = g_malloc (len + 2); /* may need one extra for trailing 0xf */ + sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2); + } + + return utf8; +} + + +static char * +sms_decode_timestamp (const guint8 *timestamp) +{ + /* YYMMDDHHMMSS+ZZ */ + char *timestr; + int quarters, hours; + + timestr = g_malloc0 (16); + sms_semi_octets_to_bcd_string (timestr, timestamp, 6); + quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf); + hours = quarters / 4; + if (timestamp[6] & 0x08) + timestr[12] = '-'; + else + timestr[12] = '+'; + timestr[13] = (hours / 10) + '0'; + timestr[14] = (hours % 10) + '0'; + /* TODO(njw): Change timestamp rep to something that includes quarter-hours */ + return timestr; +} + +static char * +sms_decode_text (const guint8 *text, int len, int dcs, int bit_offset) +{ + char *utf8; + guint8 coding = dcs & SMS_DCS_CODING_MASK; + guint8 *unpacked; + guint32 unpacked_len; + + if (coding == SMS_DCS_CODING_DEFAULT) { + unpacked = gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len); + utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len); + g_free (unpacked); + } else if (coding == SMS_DCS_CODING_UCS2) + utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL); + else if (coding == SMS_DCS_CODING_8BIT) + utf8 = g_strndup ((const char *)text, len); + else + utf8 = g_strdup (""); + + return utf8; +} + + +static GHashTable * +sms_parse_pdu (const char *hexpdu) +{ + GHashTable *properties; + gsize pdu_len; + guint8 *pdu; + int smsc_addr_num_octets, variable_length_items, msg_start_offset, + sender_addr_num_digits, sender_addr_num_octets, + tp_pid_offset, tp_dcs_offset, user_data_offset, user_data_len, + user_data_len_offset, user_data_dcs, bit_offset; + char *smsc_addr, *sender_addr, *sc_timestamp, *msg_text; + + /* Convert PDU from hex to binary */ + pdu = (guint8 *) utils_hexstr2bin (hexpdu, &pdu_len); + if (!pdu) { + mm_err("Couldn't parse PDU of SMS GET response from hex"); + return NULL; + } + + /* SMSC, in address format, precedes the TPDU */ + smsc_addr_num_octets = pdu[0]; + variable_length_items = smsc_addr_num_octets; + if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { + mm_err ("PDU too short (1): %zd vs %d", pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); + g_free (pdu); + return NULL; + } + + /* where in the PDU the actual SMS protocol message begins */ + msg_start_offset = 1 + smsc_addr_num_octets; + sender_addr_num_digits = pdu[msg_start_offset + 1]; + /* + * round the sender address length up to an even number of + * semi-octets, and thus an integral number of octets + */ + sender_addr_num_octets = (sender_addr_num_digits + 1) >> 1; + variable_length_items += sender_addr_num_octets; + if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { + mm_err ("PDU too short (2): %zd vs %d", pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); + g_free (pdu); + return NULL; + } + + tp_pid_offset = msg_start_offset + 3 + sender_addr_num_octets; + tp_dcs_offset = tp_pid_offset + 1; + + user_data_len_offset = tp_dcs_offset + 1 + SMS_TIMESTAMP_LEN; + user_data_offset = user_data_len_offset + 1; + user_data_len = pdu[user_data_len_offset]; + user_data_dcs = pdu[tp_dcs_offset]; + if ((user_data_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT) + variable_length_items += (7 * (user_data_len + 1 )) / 8; + else + variable_length_items += user_data_len; + if (pdu_len < variable_length_items + SMS_MIN_PDU_LEN) { + mm_err ("PDU too short (3): %zd vs %d", pdu_len, + variable_length_items + SMS_MIN_PDU_LEN); + g_free (pdu); + return NULL; + } + + /* Only handle SMS-DELIVER */ + if ((pdu[msg_start_offset] & SMS_TP_MTI_MASK) != SMS_TP_MTI_SMS_DELIVER) { + mm_err ("Unhandled message type: 0x%02x", pdu[msg_start_offset]); + g_free (pdu); + return NULL; + } + + /* Only handle the basic protocol identifier */ + if (pdu[tp_pid_offset] != 0) { + mm_err ("Unhandled protocol identifier: 0x%02x vs 0x00", + pdu[tp_pid_offset]); + g_free (pdu); + return NULL; + } + + smsc_addr = sms_decode_address (&pdu[1], 2 * (pdu[0] - 1)); + sender_addr = sms_decode_address (&pdu[msg_start_offset + 2], + pdu[msg_start_offset + 1]); + sc_timestamp = sms_decode_timestamp (&pdu[tp_dcs_offset + 1]); + bit_offset = 0; + if (pdu[msg_start_offset] & SMS_TP_UDHI) { + /* + * Skip over the user data headers to prevent it from being + * decoded into garbage text. + */ + int udhl; + udhl = pdu[user_data_offset] + 1; + user_data_offset += udhl; + if ((user_data_dcs & SMS_DCS_CODING_MASK) == SMS_DCS_CODING_DEFAULT) { + bit_offset = 7 - (udhl * 8) % 7; + user_data_len -= (udhl * 8 + bit_offset) / 7; + } else + user_data_len -= udhl; + } + + msg_text = sms_decode_text (&pdu[user_data_offset], user_data_len, + user_data_dcs, bit_offset); + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, + simple_free_gvalue); + g_hash_table_insert (properties, "number", + simple_string_value (sender_addr)); + g_hash_table_insert (properties, "text", + simple_string_value (msg_text)); + g_hash_table_insert (properties, "smsc", + simple_string_value (smsc_addr)); + g_hash_table_insert (properties, "timestamp", + simple_string_value (sc_timestamp)); + if (user_data_dcs & SMS_DCS_CLASS_VALID) + g_hash_table_insert (properties, "class", + simple_uint_value (user_data_dcs & + SMS_DCS_CLASS_MASK)); + g_hash_table_insert (properties, "completed", simple_boolean_value (TRUE)); + + g_free (smsc_addr); + g_free (sender_addr); + g_free (sc_timestamp); + g_free (msg_text); + g_free (pdu); + + return properties; +} + +static void +sms_get_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GHashTable *properties; + int rv, status, tpdu_len, offset; + char pdu[SMS_MAX_PDU_LEN + 1]; + + if (error) { + info->error = g_error_copy (error); + goto out; + } + + /* 344 == SMS_MAX_PDU_LEN */ + rv = sscanf (response->str, "+CMGR: %d,,%d %344s %n", + &status, &tpdu_len, pdu, &offset); + if (rv != 4) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse CMGR response (parsed %d items)", + rv); + goto out; + } + + properties = sms_parse_pdu (pdu); + if (!properties) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse SMS PDU"); + goto out; + } + + mm_callback_info_set_data (info, GS_HASH_TAG, properties, + (GDestroyNotify) g_hash_table_unref); + +out: + mm_callback_info_schedule (info); +} + +static void +sms_get_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsGetFn callback = (MMModemGsmSmsGetFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), + (GHashTable *) mm_callback_info_get_data (info, GS_HASH_TAG), + info->error, info->user_data); +} + +static void +sms_get (MMModemGsmSms *modem, + guint idx, + MMModemGsmSmsGetFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *port; + + info = mm_callback_info_new_full (MM_MODEM (modem), + sms_get_invoke, + G_CALLBACK (callback), + user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + command = g_strdup_printf ("+CMGR=%d\r\n", idx); + mm_at_serial_port_queue_command (port, command, 10, sms_get_done, info); +} + +static void +sms_delete_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +sms_delete (MMModemGsmSms *modem, + guint idx, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + command = g_strdup_printf ("+CMGD=%d\r\n", idx); + mm_at_serial_port_queue_command (port, command, 10, sms_delete_done, info); +} + +static void +sms_list_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GPtrArray *results = NULL; + int rv, status, tpdu_len, offset; + char *rstr; + + if (error) + info->error = g_error_copy (error); + else { + results = g_ptr_array_new (); + rstr = response->str; + + while (*rstr) { + GHashTable *properties; + int idx; + char pdu[SMS_MAX_PDU_LEN + 1]; + + rv = sscanf (rstr, "+CMGL: %d,%d,,%d %344s %n", + &idx, &status, &tpdu_len, pdu, &offset); + if (4 != rv) { + mm_err("Couldn't parse response to SMS LIST (%d)", rv); + break; + } + rstr += offset; + + properties = sms_parse_pdu (pdu); + if (properties) { + g_hash_table_insert (properties, "index", + simple_uint_value (idx)); + g_ptr_array_add (results, properties); + } + } + /* + * todo(njw): mm_gsm_destroy_scan_data does what we want + * (destroys a GPtrArray of g_hash_tables), but it should be + * renamed to describe that or there should be a function + * named for what we're doing here. + */ + if (results) + mm_callback_info_set_data (info, "list-sms", results, + mm_gsm_destroy_scan_data); + } + + mm_callback_info_schedule (info); +} + +static void +sms_list_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsListFn callback = (MMModemGsmSmsListFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), + (GPtrArray *) mm_callback_info_get_data (info, "list-sms"), + info->error, info->user_data); +} + +static void +sms_list (MMModemGsmSms *modem, + MMModemGsmSmsListFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *port; + + info = mm_callback_info_new_full (MM_MODEM (modem), + sms_list_invoke, + G_CALLBACK (callback), + user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + command = g_strdup_printf ("+CMGL=4\r\n"); + mm_at_serial_port_queue_command (port, command, 10, sms_list_done, info); +} + MMAtSerialPort * mm_generic_gsm_get_at_port (MMGenericGsm *modem, MMPortType ptype) @@ -4063,7 +4533,7 @@ ussd_respond (MMModemGsmUssd *modem, mm_callback_info_schedule (info); return; } - + ussd_send (modem, command, callback, user_data); return; } @@ -4364,7 +4834,7 @@ simple_connect (MMModemSimple *simple, g_free (data_device); info = mm_callback_info_new (MM_MODEM (simple), callback, user_data); - mm_callback_info_set_data (info, "simple-connect-properties", + mm_callback_info_set_data (info, "simple-connect-properties", g_hash_table_ref (properties), (GDestroyNotify) g_hash_table_unref); @@ -4390,6 +4860,18 @@ simple_uint_value (guint32 i) return val; } +static GValue * +simple_boolean_value (gboolean b) +{ + GValue *val; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_BOOLEAN); + g_value_set_boolean (val, b); + + return val; +} + static GValue * simple_string_value (const char *str) { @@ -4728,6 +5210,9 @@ static void modem_gsm_sms_init (MMModemGsmSms *class) { class->send = sms_send; + class->get = sms_get; + class->delete = sms_delete; + class->list = sms_list; } static void @@ -4825,6 +5310,7 @@ set_property (GObject *object, guint prop_id, case MM_GENERIC_GSM_PROP_USSD_STATE: case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST: case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION: + case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -4932,6 +5418,10 @@ get_property (GObject *object, guint prop_id, case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION: g_value_set_string (value, ""); break; + case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: + /* By default, try to set XOFF/XON flow control */ + g_value_set_string (value, "+IFC=1,1"); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5075,5 +5565,13 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) "Optional initialization command (errors ignored)", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD, + g_param_spec_string (MM_GENERIC_GSM_FLOW_CONTROL_CMD, + "FlowControlCommand", + "Flow control configuration command (errors ignored)", + "+IFC=1,1", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index 5712660..c6cb7fb 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -37,6 +37,7 @@ #define MM_GENERIC_GSM_POWER_DOWN_CMD "power-down-cmd" #define MM_GENERIC_GSM_INIT_CMD "init-cmd" #define MM_GENERIC_GSM_INIT_CMD_OPTIONAL "init-cmd-optional" +#define MM_GENERIC_GSM_FLOW_CONTROL_CMD "flow-control-cmd" typedef enum { MM_GENERIC_GSM_PROP_FIRST = 0x2000, @@ -57,6 +58,7 @@ typedef enum { MM_GENERIC_GSM_PROP_USSD_STATE, MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST, MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION, + MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD } MMGenericGsmProp; typedef enum { diff --git a/src/mm-log.c b/src/mm-log.c index bcf806a..779afe7 100644 --- a/src/mm-log.c +++ b/src/mm-log.c @@ -77,8 +77,8 @@ _mm_log (const char *loc, g_get_current_time (&tv); snprintf (&tsbuf[0], sizeof (tsbuf), " [%09ld.%06ld]", tv.tv_sec, tv.tv_usec); } else if (ts_flags == TS_FLAG_REL) { - time_t secs; - suseconds_t usecs; + glong secs; + glong usecs; g_get_current_time (&tv); secs = tv.tv_sec - rel_start.tv_sec; @@ -92,23 +92,23 @@ _mm_log (const char *loc, } if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) - prefix = "debug"; + prefix = ""; else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) - prefix = "info"; + prefix = " "; else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) { - prefix = "warn"; + prefix = " "; syslog_priority = LOG_WARNING; } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { - prefix = "err"; + prefix = ""; syslog_priority = LOG_ERR; } else g_warn_if_reached (); if (prefix) { if (log_level & LOGL_DEBUG) - snprintf (msgbuf, sizeof (msgbuf), "<%s>%s [%s] %s(): %s\n", prefix, tsbuf, loc, func, msg); + snprintf (msgbuf, sizeof (msgbuf), "%s%s [%s] %s(): %s\n", prefix, tsbuf, loc, func, msg); else - snprintf (msgbuf, sizeof (msgbuf), "<%s>%s %s\n", prefix, tsbuf, msg); + snprintf (msgbuf, sizeof (msgbuf), "%s%s %s\n", prefix, tsbuf, msg); if (logfd < 0) syslog (syslog_priority, "%s", msgbuf); diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c index ee5e1c8..9afc1bf 100644 --- a/src/mm-modem-base.c +++ b/src/mm-modem-base.c @@ -526,12 +526,12 @@ mm_modem_base_get_card_info (MMModemBase *self, MMModemBasePrivate *priv; MMCallbackInfo *info; gboolean cached = FALSE; - GError *error = port_error; + GError *error = NULL; 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)); + /* Either we get a proper AT port, or we get a port_error */ + g_return_if_fail ((port != NULL && MM_IS_AT_SERIAL_PORT (port)) || port_error != NULL); g_return_if_fail (callback != NULL); priv = MM_MODEM_BASE_GET_PRIVATE (self); diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c index 722918e..485e5f2 100644 --- a/src/mm-modem-cdma.c +++ b/src/mm-modem-cdma.c @@ -34,6 +34,7 @@ static void impl_modem_cdma_activate_manual (MMModemCdma *modem, DBusGMethodInvo enum { SIGNAL_QUALITY, REGISTRATION_STATE_CHANGED, + ACTIVATION_STATE_CHANGED, LAST_SIGNAL }; @@ -348,6 +349,8 @@ mm_modem_cdma_emit_registration_state_changed (MMModemCdma *self, /*****************************************************************************/ +#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) + static void mm_modem_cdma_init (gpointer g_iface) { @@ -385,6 +388,15 @@ mm_modem_cdma_init (gpointer g_iface) mm_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + signals[ACTIVATION_STATE_CHANGED] = + g_signal_new ("activation-state-changed", + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMModemCdma, registration_state_changed), + NULL, NULL, + mm_marshal_VOID__UINT_UINT_BOXED, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, DBUS_TYPE_G_MAP_OF_VARIANT); + initialized = TRUE; } diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c index 75ca7de..c152ddf 100644 --- a/src/mm-modem-gsm-network.c +++ b/src/mm-modem-gsm-network.c @@ -21,6 +21,7 @@ #include "mm-errors.h" #include "mm-callback-info.h" #include "mm-marshal.h" +#include "mm-utils.h" static void impl_gsm_modem_register (MMModemGsmNetwork *modem, const char *network_id, @@ -473,39 +474,11 @@ impl_gsm_modem_get_signal_quality (MMModemGsmNetwork *modem, mm_modem_gsm_network_get_signal_quality (modem, uint_call_done, context); } -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 impl_gsm_modem_set_band (MMModemGsmNetwork *modem, MMModemGsmBand band, DBusGMethodInvocation *context) { - if (!check_for_single_value (band)) { - GError *error; - - error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, - "Invalid arguments (more than one value given)"); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - mm_modem_gsm_network_set_band (modem, band, async_call_done, context); } @@ -521,7 +494,7 @@ impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem, MMModemDeprecatedMode old_mode, DBusGMethodInvocation *context) { - if (!check_for_single_value (old_mode)) { + if (!utils_check_for_single_value (old_mode)) { GError *error; error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, diff --git a/src/mm-modem-gsm-sms.c b/src/mm-modem-gsm-sms.c index d74c7b3..ab20d3e 100644 --- a/src/mm-modem-gsm-sms.c +++ b/src/mm-modem-gsm-sms.c @@ -103,6 +103,34 @@ async_call_not_supported (MMModemGsmSms *self, mm_callback_info_schedule (info); } +static void +sms_get_done (MMModemGsmSms *self, + GHashTable *properties, + 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, properties); +} + +static void +sms_list_done (MMModemGsmSms *self, + GPtrArray *results, + 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, results); +} + /*****************************************************************************/ void @@ -127,6 +155,110 @@ mm_modem_gsm_sms_send (MMModemGsmSms *self, } +static void +sms_get_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsGetFn callback = (MMModemGsmSmsGetFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), NULL, info->error, info->user_data); +} + +void +mm_modem_gsm_sms_get (MMModemGsmSms *self, + guint idx, + MMModemGsmSmsGetFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->get) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->get (self, idx, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), + sms_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_gsm_sms_delete (MMModemGsmSms *self, + guint idx, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->delete) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->delete (self, idx, callback, user_data); + else + async_call_not_supported (self, callback, user_data); +} + +static void +sms_list_invoke (MMCallbackInfo *info) +{ + MMModemGsmSmsListFn callback = (MMModemGsmSmsListFn) info->callback; + + callback (MM_MODEM_GSM_SMS (info->modem), NULL, info->error, info->user_data); +} + +void +mm_modem_gsm_sms_list (MMModemGsmSms *self, + MMModemGsmSmsListFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_SMS_GET_INTERFACE (self)->list) + MM_MODEM_GSM_SMS_GET_INTERFACE (self)->list (self, callback, user_data); + else { + MMCallbackInfo *info; + + info = mm_callback_info_new_full (MM_MODEM (self), + sms_list_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_gsm_sms_received (MMModemGsmSms *self, + guint idx, + gboolean complete) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + + g_signal_emit (self, signals[SMS_RECEIVED], 0, + idx, + complete); +} + +void +mm_modem_gsm_sms_completed (MMModemGsmSms *self, + guint idx, + gboolean complete) +{ + g_return_if_fail (MM_IS_MODEM_GSM_SMS (self)); + + g_signal_emit (self, signals[COMPLETED], 0, + idx, + complete); +} + /*****************************************************************************/ typedef struct { @@ -144,7 +276,8 @@ sms_auth_info_destroy (gpointer data) { SmsAuthInfo *info = data; - g_hash_table_destroy (info->hash); + if (info->hash) + g_hash_table_destroy (info->hash); g_free (info->str); memset (info, 0, sizeof (SmsAuthInfo)); g_free (info); @@ -211,14 +344,18 @@ sms_delete_auth_cb (MMAuthRequest *req, gpointer user_data) { MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner); + SmsAuthInfo *info = user_data; GError *error = NULL; + guint idx; /* 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); + } else { + idx = info->num1; + mm_modem_gsm_sms_delete (self, idx, async_call_done, context); + } } static void @@ -253,14 +390,18 @@ sms_get_auth_cb (MMAuthRequest *req, gpointer user_data) { MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner); + SmsAuthInfo *info = user_data; + guint idx; 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); + } else { + idx = info->num1; + mm_modem_gsm_sms_get (self, idx, sms_get_done, context); + } } static void @@ -368,7 +509,7 @@ sms_list_auth_cb (MMAuthRequest *req, dbus_g_method_return_error (context, error); g_error_free (error); } else - async_call_not_supported (self, async_call_done, context); + mm_modem_gsm_sms_list (self, sms_list_done, context); } static void diff --git a/src/mm-modem-gsm-sms.h b/src/mm-modem-gsm-sms.h index 79a5bb0..41684d7 100644 --- a/src/mm-modem-gsm-sms.h +++ b/src/mm-modem-gsm-sms.h @@ -25,6 +25,16 @@ typedef struct _MMModemGsmSms MMModemGsmSms; +typedef void (*MMModemGsmSmsGetFn) (MMModemGsmSms *modem, + GHashTable *properties, + GError *error, + gpointer user_data); + +typedef void (*MMModemGsmSmsListFn) (MMModemGsmSms *modem, + GPtrArray *resultlist, + GError *error, + gpointer user_data); + struct _MMModemGsmSms { GTypeInterface g_iface; @@ -38,6 +48,20 @@ struct _MMModemGsmSms { MMModemFn callback, gpointer user_data); + void (*get) (MMModemGsmSms *modem, + guint32 index, + MMModemGsmSmsGetFn callback, + gpointer user_data); + + void (*delete) (MMModemGsmSms *modem, + guint32 index, + MMModemFn callback, + gpointer user_data); + + void (*list) (MMModemGsmSms *modem, + MMModemGsmSmsListFn callback, + gpointer user_data); + /* Signals */ void (*sms_received) (MMModemGsmSms *self, guint32 index, @@ -59,4 +83,27 @@ void mm_modem_gsm_sms_send (MMModemGsmSms *self, MMModemFn callback, gpointer user_data); +void mm_modem_gsm_sms_get (MMModemGsmSms *self, + guint idx, + MMModemGsmSmsGetFn callback, + gpointer user_data); + +void mm_modem_gsm_sms_delete (MMModemGsmSms *self, + guint idx, + MMModemFn callback, + gpointer user_data); + +void mm_modem_gsm_sms_list (MMModemGsmSms *self, + MMModemGsmSmsListFn callback, + gpointer user_data); + +void mm_modem_gsm_sms_received (MMModemGsmSms *self, + guint idx, + gboolean complete); + +void mm_modem_gsm_sms_completed (MMModemGsmSms *self, + guint idx, + gboolean complete); + + #endif /* MM_MODEM_GSM_SMS_H */ diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h index a427d35..83dfb31 100644 --- a/src/mm-modem-gsm.h +++ b/src/mm-modem-gsm.h @@ -74,8 +74,9 @@ typedef enum { 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_U2600 = 0x00002000, /* WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) */ - MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U1900 + MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U2600 } MMModemGsmBand; diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index f13b4f2..f6a0ffa 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -235,7 +235,7 @@ mm_gsm_destroy_scan_data (gpointer data) #define CREG3 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)" /* +CREG: ,,, (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]*)" +#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)" /* +CREG: ,,, (ETSI 27.007 CREG=2 unsolicited) */ #define CREG5 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})" diff --git a/src/mm-modem.c b/src/mm-modem.c index f823472..d535166 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -751,7 +751,7 @@ mm_modem_set_state (MMModem *self, if (new_state != old_state) { g_object_set (G_OBJECT (self), MM_MODEM_STATE, new_state, NULL); - g_signal_emit_by_name (G_OBJECT (self), "state-changed", new_state, old_state, reason); + g_signal_emit_by_name (G_OBJECT (self), "state-changed", old_state, new_state, reason); dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG); if (dbus_path) { diff --git a/src/mm-modem.h b/src/mm-modem.h index 7f0bf58..c478f70 100644 --- a/src/mm-modem.h +++ b/src/mm-modem.h @@ -207,8 +207,8 @@ struct _MMModem { /* Signals */ void (*state_changed) (MMModem *self, - MMModemState new_state, MMModemState old_state, + MMModemState new_state, MMModemStateReason reason); }; diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c index 8d32e2a..336f78c 100644 --- a/src/mm-plugin-base.c +++ b/src/mm-plugin-base.c @@ -92,6 +92,14 @@ G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OB #define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskPrivate)) +typedef struct { + char *command; + guint32 tries; + guint32 delay_seconds; + MMBaseSupportsTaskCustomInitResultFunc callback; + gpointer callback_data; +} CustomInit; + typedef struct { MMPluginBase *plugin; GUdevDevice *port; @@ -110,11 +118,9 @@ typedef struct { char *probe_resp; GError *probe_error; - char *custom_init; - guint32 custom_init_max_tries; - guint32 custom_init_tries; - guint32 custom_init_delay_seconds; - gboolean custom_init_fail_if_timeout; + /* Custom init commands plugins might want */ + GSList *custom; + GSList *cur_custom; /* Pointer to current custom init command */ MMSupportsPortResultFunc callback; gpointer callback_data; @@ -225,24 +231,28 @@ mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, } void -mm_plugin_base_supports_task_set_custom_init_command (MMPluginBaseSupportsTask *task, +mm_plugin_base_supports_task_add_custom_init_command (MMPluginBaseSupportsTask *task, const char *cmd, guint32 delay_seconds, - guint32 max_tries, - gboolean fail_if_timeout) + MMBaseSupportsTaskCustomInitResultFunc callback, + gpointer callback_data) { MMPluginBaseSupportsTaskPrivate *priv; + CustomInit *custom; g_return_if_fail (task != NULL); g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task)); + g_return_if_fail (callback != NULL); priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); - g_free (priv->custom_init); - priv->custom_init = g_strdup (cmd); - priv->custom_init_max_tries = max_tries; - priv->custom_init_delay_seconds = delay_seconds; - priv->custom_init_fail_if_timeout = fail_if_timeout; + custom = g_malloc0 (sizeof (*custom)); + custom->command = g_strdup (cmd); + custom->delay_seconds = delay_seconds ? delay_seconds : 3; + custom->callback = callback; + custom->callback_data = callback_data; + + priv->custom = g_slist_append (priv->custom, custom); } static void @@ -254,6 +264,7 @@ static void supports_task_dispose (GObject *object) { MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object); + GSList *iter; if (MM_IS_SERIAL_PORT (priv->probe_port)) mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->probe_port)); @@ -263,7 +274,14 @@ supports_task_dispose (GObject *object) g_free (priv->driver); g_free (priv->probe_resp); g_clear_error (&(priv->probe_error)); - g_free (priv->custom_init); + + for (iter = priv->custom; iter; iter = g_slist_next (iter)) { + CustomInit *custom = iter->data; + + g_free (custom->command); + memset (custom, 0, sizeof (*custom)); + g_free (custom); + } if (priv->open_id) g_source_remove (priv->open_id); @@ -721,6 +739,12 @@ parse_response (MMAtSerialPort *port, task_priv->probe_id = g_idle_add (handle_probe_response, task); } +static void +start_generic_probing (MMPluginBaseSupportsTask *task, MMAtSerialPort *port) +{ + mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); +} + static void flash_done (MMSerialPort *port, GError *error, gpointer user_data); static void @@ -731,24 +755,43 @@ custom_init_response (MMAtSerialPort *port, { MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + CustomInit *custom = task_priv->cur_custom->data; + gboolean retry = FALSE; + gboolean fail = FALSE; + guint32 level = 0; - if (error) { - task_priv->custom_init_tries++; - if (task_priv->custom_init_tries < task_priv->custom_init_max_tries) { - /* Try the custom command again */ - 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_ERROR_RESPONSE_TIMEOUT)) { - probe_complete (task); - return; - } - } + custom->tries++; + retry = custom->callback (task, response, error, custom->tries, &fail, &level, custom->callback_data); + + if (fail) { + /* Plugin said to fail the probe */ + probe_complete (task); + return; + } + + if (level > 0) { + /* Plugin supports the modem */ + task_priv->probed_caps = level; + probe_complete (task); + return; + } + + if (retry) { + /* Try the custom command again */ + flash_done (MM_SERIAL_PORT (port), NULL, task); + return; + } + + /* Any more custom init commands? */ + task_priv->cur_custom = g_slist_next (task_priv->cur_custom); + if (task_priv->cur_custom) { + /* There are more custom init commands */ + flash_done (MM_SERIAL_PORT (port), NULL, task); + return; } - /* Otherwise proceed to probing */ - mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data); + /* Otherwise continue with generic probing */ + start_generic_probing (task, port); } static void @@ -756,20 +799,19 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data) { MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); - guint32 delay_secs = task_priv->custom_init_delay_seconds; /* Send the custom init command if any */ - if (task_priv->custom_init) { - if (!delay_secs) - delay_secs = 3; + if (task_priv->cur_custom) { + CustomInit *custom = task_priv->cur_custom->data; + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), - task_priv->custom_init, - delay_secs, + custom->command, + custom->delay_seconds, custom_init_response, - user_data); + task); } else { /* Otherwise start normal probing */ - custom_init_response (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data); + start_generic_probing (task, MM_AT_SERIAL_PORT (port)); } } @@ -820,6 +862,7 @@ try_open (gpointer user_data) gboolean mm_plugin_base_probe_port (MMPluginBase *self, MMPluginBaseSupportsTask *task, + guint64 send_delay_us, GError **error) { MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); @@ -844,8 +887,9 @@ mm_plugin_base_probe_port (MMPluginBase *self, } g_object_set (serial, - MM_SERIAL_PORT_SEND_DELAY, (guint64) 100000, + MM_SERIAL_PORT_SEND_DELAY, send_delay_us, MM_PORT_CARRIER_DETECT, FALSE, + MM_SERIAL_PORT_SPEW_CONTROL, TRUE, NULL); mm_at_serial_port_set_response_parser (serial, @@ -855,6 +899,7 @@ mm_plugin_base_probe_port (MMPluginBase *self, /* Open the port */ task_priv->probe_port = serial; + task_priv->cur_custom = task_priv->custom; task_priv->open_id = g_idle_add (try_open, task); return TRUE; } @@ -933,6 +978,10 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, pid = g_udev_device_get_sysfs_attr (parent, "card_id"); if (!vid || !pid) goto out; + } else if (!strcmp (parent_subsys, "platform")) { + /* Platform devices don't usually have a VID/PID */ + success = TRUE; + goto out; } } } diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h index a32d53b..a5440a5 100644 --- a/src/mm-plugin-base.h +++ b/src/mm-plugin-base.h @@ -56,6 +56,27 @@ typedef struct { GType mm_plugin_base_supports_task_get_type (void); +/* + * response: the response string from the modem, if no error occurred + * error: the error returned by the modem or serial stack, if any + * tries: number of times the custom init command has been sent to the modem + * out_fail: on return, TRUE means fail the probe and close the port + * out_level: if the custom init command has determined that the modem is + * supported, return the support level here, and probing will cease + * + * Function should return TRUE if the custom init command should be retried, + * FALSE if it should not. If FALSE is returned, generic probing will continue + * if out_fail == FALSE and out_level == 0, otherwise if out_fail == FALSE + * probing will stop, or if out_level > 0 the port will be claimed. + */ +typedef gboolean (*MMBaseSupportsTaskCustomInitResultFunc) (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data); + MMPlugin *mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *task); GUdevDevice *mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task); @@ -69,11 +90,11 @@ guint32 mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSuppor void mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, guint32 level); -void mm_plugin_base_supports_task_set_custom_init_command (MMPluginBaseSupportsTask *task, +void mm_plugin_base_supports_task_add_custom_init_command (MMPluginBaseSupportsTask *task, const char *cmd, guint32 delay_seconds, - guint32 max_tries, - gboolean fail_if_timeout); + MMBaseSupportsTaskCustomInitResultFunc callback, + gpointer callback_data); #define MM_TYPE_PLUGIN_BASE (mm_plugin_base_get_type ()) #define MM_PLUGIN_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE, MMPluginBase)) @@ -108,6 +129,7 @@ struct _MMPluginBaseClass { void (*cancel_task) (MMPluginBase *plugin, MMPluginBaseSupportsTask *task); + /* Lets plugins read the probe response before the generic plugin processes it */ void (*handle_probe_response) (MMPluginBase *plugin, MMPluginBaseSupportsTask *task, const char *command, @@ -130,6 +152,7 @@ gboolean mm_plugin_base_get_device_ids (MMPluginBase *self, gboolean mm_plugin_base_probe_port (MMPluginBase *self, MMPluginBaseSupportsTask *task, + guint64 send_delay_us, GError **error); /* Returns TRUE if the port was previously probed, FALSE if not */ diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index 75bcce4..f83301d 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -196,13 +196,18 @@ mm_serial_parser_v0_destroy (gpointer data) } typedef struct { + /* Regular expressions for successful replies */ GRegex *regex_ok; GRegex *regex_connect; + GRegex *regex_custom_successful; + /* Regular expressions for error replies */ GRegex *regex_cme_error; + GRegex *regex_cms_error; GRegex *regex_cme_error_str; GRegex *regex_ezx_error; GRegex *regex_unknown_error; GRegex *regex_connect_failed; + GRegex *regex_custom_error; } MMSerialParserV1; gpointer @@ -216,14 +221,36 @@ 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_cme_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + parser->regex_cms_error = g_regex_new ("\\r\\n\\+CMS 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_ezx_error = g_regex_new ("\\r\\n\\MODEM ERROR: (\\d+)\\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); + parser->regex_custom_successful = NULL; + parser->regex_custom_error = NULL; + return parser; } +void +mm_serial_parser_v1_set_custom_regex (gpointer data, + GRegex *successful, + GRegex *error) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + + g_return_if_fail (parser != NULL); + + if (parser->regex_custom_successful) + g_regex_unref (parser->regex_custom_successful); + if (parser->regex_custom_error) + g_regex_unref (parser->regex_custom_error); + + parser->regex_custom_successful = successful ? g_regex_ref (successful) : NULL; + parser->regex_custom_error = error ? g_regex_ref (error) : NULL; +} + gboolean mm_serial_parser_v1_parse (gpointer data, GString *response, @@ -244,11 +271,24 @@ mm_serial_parser_v1_parse (gpointer data, /* First, check for successful responses */ - found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); - if (found) - remove_matches (parser->regex_ok, response); - else - found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL); + /* Custom successful replies first, if any */ + if (parser->regex_custom_successful) { + found = g_regex_match_full (parser->regex_custom_successful, + response->str, response->len, + 0, 0, NULL, NULL); + } + + if (!found) { + found = g_regex_match_full (parser->regex_ok, + response->str, response->len, + 0, 0, NULL, NULL); + if (found) + remove_matches (parser->regex_ok, response); + else + found = g_regex_match_full (parser->regex_connect, + response->str, response->len, + 0, 0, NULL, NULL); + } if (found) { response_clean (response); @@ -257,6 +297,21 @@ mm_serial_parser_v1_parse (gpointer data, /* Now failures */ + /* Custom error matches first, if any */ + if (parser->regex_custom_error) { + found = g_regex_match_full (parser->regex_custom_error, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + 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; + } + } + /* Numeric CME errors */ found = g_regex_match_full (parser->regex_cme_error, response->str, response->len, @@ -270,6 +325,23 @@ mm_serial_parser_v1_parse (gpointer data, goto done; } + /* Numeric CMS errors */ + /* Todo + * One should probably add message service + * errors explicitly in mm-errors.h/c + */ + found = g_regex_match_full (parser->regex_cms_error, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + 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, @@ -359,6 +431,11 @@ mm_serial_parser_v1_destroy (gpointer data) g_regex_unref (parser->regex_unknown_error); g_regex_unref (parser->regex_connect_failed); + if (parser->regex_custom_successful) + g_regex_unref (parser->regex_custom_successful); + if (parser->regex_custom_error) + g_regex_unref (parser->regex_custom_error); + g_slice_free (MMSerialParserV1, data); } diff --git a/src/mm-serial-parsers.h b/src/mm-serial-parsers.h index 3e1fb9f..92361c3 100644 --- a/src/mm-serial-parsers.h +++ b/src/mm-serial-parsers.h @@ -22,23 +22,23 @@ gpointer mm_serial_parser_v0_new (void); gboolean mm_serial_parser_v0_parse (gpointer parser, GString *response, GError **error); - void mm_serial_parser_v0_destroy (gpointer parser); -gpointer mm_serial_parser_v1_new (void); -gboolean mm_serial_parser_v1_parse (gpointer parser, - GString *response, - GError **error); - -void mm_serial_parser_v1_destroy (gpointer parser); +gpointer mm_serial_parser_v1_new (void); +void mm_serial_parser_v1_set_custom_regex (gpointer data, + GRegex *successful, + GRegex *error); +gboolean mm_serial_parser_v1_parse (gpointer parser, + GString *response, + GError **error); +void mm_serial_parser_v1_destroy (gpointer parser); gpointer mm_serial_parser_v1_e1_new (void); gboolean mm_serial_parser_v1_e1_parse (gpointer parser, GString *response, GError **error); - void mm_serial_parser_v1_e1_destroy (gpointer parser); #endif /* MM_SERIAL_PARSERS_H */ diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index bf2a98a..18a616d 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -44,6 +44,7 @@ enum { PROP_STOPBITS, PROP_SEND_DELAY, PROP_FD, + PROP_SPEW_CONTROL, LAST_PROP }; @@ -67,8 +68,9 @@ typedef struct { char parity; guint stopbits; guint64 send_delay; + gboolean spew_control; - guint queue_schedule; + guint queue_id; guint watch_id; guint timeout_id; @@ -76,6 +78,18 @@ typedef struct { guint connected_id; } MMSerialPortPrivate; +typedef struct { + GByteArray *command; + guint32 idx; + guint32 eagain_count; + gboolean started; + gboolean done; + GCallback callback; + gpointer user_data; + guint32 timeout; + gboolean cached; +} MMQueueData; + #if 0 static const char * baud_to_string (int baud) @@ -355,57 +369,67 @@ serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len } static gboolean -mm_serial_port_send_command (MMSerialPort *self, - GByteArray *command, - GError **error) +mm_serial_port_process_command (MMSerialPort *self, + MMQueueData *info, + GError **error) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - int status, i = 0; - int eagain_count = 1000; const guint8 *p; + int status, expected_status, send_len; if (priv->fd < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, - "%s", "Sending command failed: device is not enabled"); + g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "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_ERROR_SEND_FAILED, - "%s", "Sending command failed: device is connected"); + g_set_error_literal (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: device is connected"); return FALSE; } - 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; - - 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_ERROR_SEND_FAILED, - "Sending command failed: '%s'", strerror (errno)); - break; - } - } else { + /* Only print command the first time */ + if (info->started == FALSE) { + info->started = TRUE; + serial_debug (self, "-->", (const char *) info->command->data, info->command->len); + } + + if (priv->send_delay == 0) { + /* Send the whole command in one write */ + send_len = expected_status = info->command->len; + p = info->command->data; + } else { + /* Send just one byte of the command */ + send_len = expected_status = 1; + p = &info->command->data[info->idx]; + } + + /* Send a single byte of the command */ + errno = 0; + status = write (priv->fd, p, send_len); + if (status > 0) + info->idx += status; + else { + /* Error or no bytes written */ + if (errno == EAGAIN || status == 0) { + info->eagain_count--; + if (info->eagain_count <= 0) { g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, "Sending command failed: '%s'", strerror (errno)); - break; + return FALSE; } - } else - i++; - - if (priv->send_delay) - usleep (priv->send_delay); + } else { + g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED, + "Sending command failed: '%s'", strerror (errno)); + return FALSE; + } } - return i == command->len; + if (info->idx >= info->command->len) + info->done = TRUE; + + return TRUE; } static void @@ -436,35 +460,25 @@ 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 { - GByteArray *command; - GCallback callback; - gpointer user_data; - guint32 timeout; - gboolean cached; -} MMQueueData; - static void -mm_serial_port_schedule_queue_process (MMSerialPort *self) +mm_serial_port_schedule_queue_process (MMSerialPort *self, guint timeout_ms) { MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); - GSource *source; if (priv->timeout_id) { /* A command is already in progress */ return; } - if (priv->queue_schedule) { + if (priv->queue_id) { /* Already scheduled */ return; } - source = g_idle_source_new (); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->queue_schedule = g_source_get_id (source); - g_source_unref (source); + if (timeout_ms) + priv->queue_id = g_timeout_add (timeout_ms, mm_serial_port_queue_process, self); + else + priv->queue_id = g_idle_add (mm_serial_port_queue_process, self); } static gsize @@ -516,7 +530,7 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error) 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); + mm_serial_port_schedule_queue_process (self, 0); } static gboolean @@ -547,7 +561,7 @@ mm_serial_port_queue_process (gpointer data) MMQueueData *info; GError *error = NULL; - priv->queue_schedule = 0; + priv->queue_id = 0; info = (MMQueueData *) g_queue_peek_head (priv->queue); if (!info) @@ -557,23 +571,35 @@ mm_serial_port_queue_process (gpointer data) const GByteArray *cached = mm_serial_port_get_cached_reply (self, info->command); if (cached) { + /* Ensure the response array is fully empty before setting the + * cached response. */ + if (priv->response->len > 0) { + g_warning ("%s: (%s) response array is not empty when using " + "cached reply, cleaning up %u bytes", + __func__, + mm_port_get_device (MM_PORT (self)), + priv->response->len); + g_byte_array_set_size (priv->response, 0); + } + g_byte_array_append (priv->response, cached->data, cached->len); mm_serial_port_got_response (self, NULL); return FALSE; } } - if (mm_serial_port_send_command (self, info->command, &error)) { - GSource *source; - - source = g_timeout_source_new_seconds (info->timeout); - g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self))); - g_source_attach (source, NULL); - priv->timeout_id = g_source_get_id (source); - g_source_unref (source); - } else { + if (mm_serial_port_process_command (self, info, &error)) { + if (info->done) { + /* If the command is finished being sent, schedule the timeout */ + priv->timeout_id = g_timeout_add_seconds (info->timeout, + mm_serial_port_timed_out, + self); + } else { + /* Schedule the next byte of the command to be sent */ + mm_serial_port_schedule_queue_process (self, priv->send_delay / 1000); + } + } else mm_serial_port_got_response (self, error); - } return FALSE; } @@ -600,6 +626,7 @@ data_available (GIOChannel *source, char buf[SERIAL_BUF_SIZE + 1]; gsize bytes_read; GIOStatus status; + MMQueueData *info; if (condition & G_IO_HUP) { if (priv->response->len) @@ -614,6 +641,11 @@ data_available (GIOChannel *source, return TRUE; } + /* Don't read any input if the current command isn't done being sent yet */ + info = g_queue_peek_nth (priv->queue, 0); + if (info && (info->started == TRUE) && (info->done == FALSE)) + return TRUE; + do { GError *err = NULL; @@ -638,7 +670,7 @@ data_available (GIOChannel *source, } /* Make sure the response doesn't grow too long */ - if (priv->response->len > SERIAL_BUF_SIZE) { + if ((priv->response->len > SERIAL_BUF_SIZE) && priv->spew_control) { /* 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)); @@ -805,6 +837,8 @@ mm_serial_port_close (MMSerialPort *self) priv->connected_id = 0; } + mm_serial_port_flash_cancel (self); + if (priv->fd >= 0) { GTimeVal tv_start, tv_end; @@ -820,8 +854,6 @@ mm_serial_port_close (MMSerialPort *self) priv->channel = NULL; } - mm_serial_port_flash_cancel (self); - g_get_current_time (&tv_start); tcsetattr (priv->fd, TCSANOW, &priv->old_t); @@ -869,6 +901,16 @@ mm_serial_port_close (MMSerialPort *self) g_slice_free (MMQueueData, item); } g_queue_clear (priv->queue); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->queue_id) { + g_source_remove (priv->queue_id); + priv->queue_id = 0; + } } void @@ -909,6 +951,13 @@ internal_queue_command (MMSerialPort *self, info->command = g_byte_array_sized_new (command->len); g_byte_array_append (info->command, command->data, command->len); } + + /* Only accept about 3 seconds of EAGAIN for this command */ + if (priv->send_delay) + info->eagain_count = 3000000 / priv->send_delay; + else + info->eagain_count = 1000; + info->cached = cached; info->timeout = timeout_seconds; info->callback = (GCallback) callback; @@ -921,7 +970,7 @@ internal_queue_command (MMSerialPort *self, g_queue_push_tail (priv->queue, info); if (g_queue_get_length (priv->queue) == 1) - mm_serial_port_schedule_queue_process (self); + mm_serial_port_schedule_queue_process (self, 0); } void @@ -1224,6 +1273,9 @@ set_property (GObject *object, guint prop_id, case PROP_SEND_DELAY: priv->send_delay = g_value_get_uint64 (value); break; + case PROP_SPEW_CONTROL: + priv->spew_control = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1255,6 +1307,9 @@ get_property (GObject *object, guint prop_id, case PROP_SEND_DELAY: g_value_set_uint64 (value, priv->send_delay); break; + case PROP_SPEW_CONTROL: + g_value_set_boolean (value, priv->spew_control); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1264,6 +1319,13 @@ get_property (GObject *object, guint prop_id, static void dispose (GObject *object) { + MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + if (mm_serial_port_is_open (MM_SERIAL_PORT (object))) mm_serial_port_close_force (MM_SERIAL_PORT (object)); @@ -1346,10 +1408,18 @@ mm_serial_port_class_init (MMSerialPortClass *klass) (object_class, PROP_SEND_DELAY, g_param_spec_uint64 (MM_SERIAL_PORT_SEND_DELAY, "SendDelay", - "Send delay", + "Send delay for each byte in microseconds", 0, G_MAXUINT64, 0, G_PARAM_READWRITE)); + g_object_class_install_property + (object_class, PROP_SPEW_CONTROL, + g_param_spec_boolean (MM_SERIAL_PORT_SPEW_CONTROL, + "SpewControl", + "Spew control", + FALSE, + G_PARAM_READWRITE)); + /* Signals */ g_signal_new ("buffer-full", G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h index 5fee1b4..57ef94b 100644 --- a/src/mm-serial-port.h +++ b/src/mm-serial-port.h @@ -30,12 +30,13 @@ #define MM_IS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL_PORT)) #define MM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL_PORT, MMSerialPortClass)) -#define MM_SERIAL_PORT_BAUD "baud" -#define MM_SERIAL_PORT_BITS "bits" -#define MM_SERIAL_PORT_PARITY "parity" -#define MM_SERIAL_PORT_STOPBITS "stopbits" -#define MM_SERIAL_PORT_SEND_DELAY "send-delay" -#define MM_SERIAL_PORT_FD "fd" /* Construct-only */ +#define MM_SERIAL_PORT_BAUD "baud" +#define MM_SERIAL_PORT_BITS "bits" +#define MM_SERIAL_PORT_PARITY "parity" +#define MM_SERIAL_PORT_STOPBITS "stopbits" +#define MM_SERIAL_PORT_SEND_DELAY "send-delay" +#define MM_SERIAL_PORT_FD "fd" /* Construct-only */ +#define MM_SERIAL_PORT_SPEW_CONTROL "spew-control" /* Construct-only */ typedef struct _MMSerialPort MMSerialPort; typedef struct _MMSerialPortClass MMSerialPortClass; diff --git a/src/mm-utils.c b/src/mm-utils.c index c435d1d..236c2d9 100644 --- a/src/mm-utils.c +++ b/src/mm-utils.c @@ -90,3 +90,20 @@ utils_bin2hexstr (const guint8 *bin, gsize len) return g_string_free (ret, FALSE); } +gboolean +utils_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; +} diff --git a/src/mm-utils.h b/src/mm-utils.h index 1b9b328..be77f8a 100644 --- a/src/mm-utils.h +++ b/src/mm-utils.h @@ -22,5 +22,7 @@ char *utils_hexstr2bin (const char *hex, gsize *out_len); char *utils_bin2hexstr (const guint8 *bin, gsize len); +gboolean utils_check_for_single_value (guint32 value); + #endif /* MM_UTILS_H */ diff --git a/src/tests/test-charsets.c b/src/tests/test-charsets.c index 656b80c..70c796a 100644 --- a/src/tests/test-charsets.c +++ b/src/tests/test-charsets.c @@ -98,7 +98,7 @@ test_unpack_gsm7 (void *f, gpointer d) guint8 *unpacked; guint32 unpacked_len = 0; - unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + unpacked = gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); @@ -110,17 +110,16 @@ static void test_unpack_gsm7_7_chars (void *f, gpointer d) { static const guint8 gsm[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 }; - static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x00 }; + static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75}; guint8 *unpacked; guint32 unpacked_len = 0; /* Tests the edge case where there are 7 bits left in the packed * buffer but those 7 bits do not contain a character. In this case - * we expect a trailing NULL byte and the caller must know enough about - * the intended message to remove it when required. + * we expect to get the number of characters that were specified. */ - unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + unpacked = gsm_unpack (gsm, 7 , 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, sizeof (expected)); g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); @@ -153,7 +152,7 @@ test_unpack_gsm7_all_chars (void *f, gpointer d) guint32 unpacked_len = 0; int i; - unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + unpacked = gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len); g_assert (unpacked); g_assert_cmpint (unpacked_len, ==, 148); diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 946916f..d07f0d4 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -732,6 +732,16 @@ test_creg2_s8500_wave_unsolicited (void *f, gpointer d) test_creg_match ("Samsung Wave S8500 CREG=2", FALSE, reply, data, &result); } +static void +test_creg2_gobi_weird_solicited (void *f, gpointer d) +{ + TestData *data = (TestData *) d; + const char *reply = "\r\n+CREG: 2,1, 0 5, 2715\r\n"; + const CregResult result = { 1, 0x0000, 0x2715, -1, 4, FALSE}; + + test_creg_match ("Qualcomm Gobi 1000 CREG=2", TRUE, reply, data, &result); +} + static void test_cscs_icon225_support_response (void *f, gpointer d) { @@ -1251,6 +1261,7 @@ int main (int argc, char **argv) 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_creg2_s8500_wave_unsolicited, data)); + g_test_suite_add (suite, TESTCASE (test_creg2_gobi_weird_solicited, data)); g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, data)); g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, data)); diff --git a/test/Makefile.am b/test/Makefile.am index 2fbc519..8249131 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -25,5 +25,6 @@ EXTRA_DIST = \ info.py \ list-modems.py \ location.py \ - mm-send-sms.py + mm-send-sms.py \ + send-pin.py diff --git a/test/send-pin.py b/test/send-pin.py new file mode 100755 index 0000000..3cf47ee --- /dev/null +++ b/test/send-pin.py @@ -0,0 +1,65 @@ +#!/usr/bin/python +# -*- Mode: python; 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) 2009 - 2010 Red Hat, Inc. +# + +import sys, dbus, os + +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +DBUS_INTERFACE_PROPS='org.freedesktop.DBus.Properties' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' +MM_DBUS_INTERFACE_GSM_CARD='org.freedesktop.ModemManager.Modem.Gsm.Card' + +if len(sys.argv) != 3: + print "Usage: " + os._exit(1) +if not len(sys.argv[2]) in range(4,9): + print "PIN must be between 4 or 8 characters inclusive" + os._exit(1) +if not sys.argv[2].isdigit(): + print "PIN must be numeric" + os._exit(1) + +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) +props = dbus.Interface(proxy, dbus_interface=DBUS_INTERFACE_PROPS) +req = props.Get(MM_DBUS_INTERFACE_MODEM, "UnlockRequired") +if req == "": + print "SIM unlocked" + os._exit(0) + +print "Unlock Required: %s" % req +if req != "sim-pin": + print "Only sim-pin unlock supported for now" + os._exit(1) + +# Unlock the SIM +print "Unlocking with PIN" +card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_GSM_CARD) +try: + card.SendPin(sys.argv[2]) +except Exception, e: + print "Unlock failed: %s" % e + os._exit(1) + +# Check to make sure it actually got unlocked +req = props.Get(MM_DBUS_INTERFACE_MODEM, "UnlockRequired") +if req != "": + print "Unlock not successful: %s" % req + os._exit(1) + +print "Unlock successful" +os._exit(0) + -- cgit v1.2.3