aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2011-08-07 01:47:27 +0200
committerGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:27 +0100
commit95e75c9fd39d5c16c79add762ca578e0360509d6 (patch)
tree2f09dec06f41503d32a3deade89123ba3ce267c2
parent59ed3390b9a798ff8bf9133cbc28c4539ad99f42 (diff)
parenta09050a7f63a262bf90dcb1c7a41f9cfd205db43 (diff)
Imported Debian patch 0.5-1debian/0.5-1
-rw-r--r--NEWS11
-rw-r--r--README4
-rwxr-xr-xconfigure20
-rw-r--r--configure.ac2
-rw-r--r--debian/changelog32
-rw-r--r--debian/control15
-rw-r--r--debian/modemmanager.install3
-rw-r--r--debian/patches/lp700316_usb_blacklist.patch19
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules23
-rw-r--r--debian/ubuntu/modemmanager.upstart14
-rw-r--r--include/mm-modem.h2
-rw-r--r--introspection/org.freedesktop.ModemManager.Modem.Gsm.xml3
-rw-r--r--introspection/org.freedesktop.ModemManager.Modem.Simple.xml5
-rw-r--r--introspection/org.freedesktop.ModemManager.xml12
-rw-r--r--plugins/Makefile.am3
-rw-r--r--plugins/Makefile.in5
-rw-r--r--plugins/mm-modem-gobi-gsm.c53
-rw-r--r--plugins/mm-modem-huawei-gsm.c51
-rw-r--r--plugins/mm-modem-icera.c75
-rw-r--r--plugins/mm-modem-mbm.c29
-rw-r--r--plugins/mm-modem-nokia.c4
-rw-r--r--plugins/mm-modem-option-utils.c130
-rwxr-xr-xplugins/mm-modem-samsung-gsm.c8
-rw-r--r--plugins/mm-modem-sierra-gsm.c2
-rw-r--r--plugins/mm-modem-zte.c25
-rw-r--r--plugins/mm-plugin-option.c9
-rwxr-xr-xplugins/mm-plugin-samsung.c2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.in16
-rw-r--r--src/mm-callback-info.c8
-rw-r--r--src/mm-callback-info.h1
-rw-r--r--src/mm-generic-cdma.c167
-rw-r--r--src/mm-generic-gsm.c660
-rw-r--r--src/mm-generic-gsm.h2
-rw-r--r--src/mm-log.c37
-rw-r--r--src/mm-log.h2
-rw-r--r--src/mm-manager.c20
-rw-r--r--src/mm-modem-gsm-ussd.c26
-rw-r--r--src/mm-modem-gsm-ussd.h20
-rw-r--r--src/mm-plugin-base.c6
-rw-r--r--src/mm-serial-port.c120
-rw-r--r--src/mm-serial-port.h4
-rw-r--r--src/mm-sms-utils.c389
-rw-r--r--src/mm-sms-utils.h25
-rw-r--r--src/tests/Makefile.am14
-rw-r--r--src/tests/Makefile.in44
-rw-r--r--src/tests/test-sms.c429
48 files changed, 2050 insertions, 506 deletions
diff --git a/NEWS b/NEWS
index f9e851f..c03ac4b 100644
--- a/NEWS
+++ b/NEWS
@@ -38,8 +38,17 @@ Overview of changes in ModemManager 0.5
- Add support for Simtech-based Prolink PH-300
- Add support for Alcatel X200
- Fix various crashes when modem is removed
+- Improvements for Samsung modems
+- Support access technology reporting for Qualcomm Gobi modems
+- Fix communication with Nokia N900 devices
+- Support multiple CDMA Rm protocols
+- Fix handling of Option access technology reporting
+- Fix handling of CDMA EVDO registration states
+- Fix problems reconnecting Ericsson F5521gw modems
+- Better handling of some Android handset modem
Fixed bugs:
rh#597296 rh#583691 rh#597088 bgo#626421 rh#585394 bgo#628105 bgo#627935
bgo#621815 rh#632516 lp:682282 bgo#590798 lp:673457 bgo#636040 bgo #636438
-bgo#638038 bgo#637140 rh#544121 kde#266807 novell#674022
+bgo#638038 bgo#637140 rh#544121 kde#266807 novell#674022 rh#583691
+lp:765516 bgo#641661 bgo#652682 bgo#650740 bgo#652910 bgo#637327
diff --git a/README b/README
index e8f2f4f..a95b6db 100644
--- a/README
+++ b/README
@@ -13,7 +13,7 @@ program (tests/mm-test.py) that demonstrates the basic API usage.
Implementation.
ModemManager is a DBus system bus activated service (meaning it's started
automatically when a request arrives). It is written in C. The devices are
-queried from HAL and automatically updated based on hardware events. There's
+queried from udev and automatically updated based on hardware events. There's
a GInterface (MMModem) that defines the modem interface and any device specific
implementation must implement it. There are two generic MMModem implementations
to support the basic operations (one for GSM, one for CDMA,) which are common
@@ -22,7 +22,7 @@ for all cards.
Plugins.
Plugins are loaded on startup, and must implement the MMPlugin interface. It
consists of a couple of methods which tell the daemon whether the plugin
-supports a HAL UDI and to create custom MMModem implementations. It most likely
+supports a port and to create custom MMModem implementations. It most likely
makes sense to derive custom modem implementations from one of the generic
classes and just add (or override) operations which are not standard. There's a
fully working plugin in the plugins/ directory for Huawei cards that can be
diff --git a/configure b/configure
index 0e6de57..f462b55 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for ModemManager 0.4.997.
+# Generated by GNU Autoconf 2.68 for ModemManager 0.5.
#
# Report bugs to <dcbw@redhat.com>.
#
@@ -570,8 +570,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='ModemManager'
PACKAGE_TARNAME='ModemManager'
-PACKAGE_VERSION='0.4.997'
-PACKAGE_STRING='ModemManager 0.4.997'
+PACKAGE_VERSION='0.5'
+PACKAGE_STRING='ModemManager 0.5'
PACKAGE_BUGREPORT='dcbw@redhat.com'
PACKAGE_URL=''
@@ -1373,7 +1373,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures ModemManager 0.4.997 to adapt to many kinds of systems.
+\`configure' configures ModemManager 0.5 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1443,7 +1443,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of ModemManager 0.4.997:";;
+ short | recursive ) echo "Configuration of ModemManager 0.5:";;
esac
cat <<\_ACEOF
@@ -1574,7 +1574,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-ModemManager configure 0.4.997
+ModemManager configure 0.5
generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1943,7 +1943,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by ModemManager $as_me 0.4.997, which was
+It was created by ModemManager $as_me 0.5, which was
generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@
@@ -2758,7 +2758,7 @@ fi
# Define the identity of the package.
PACKAGE='ModemManager'
- VERSION='0.4.997'
+ VERSION='0.5'
cat >>confdefs.h <<_ACEOF
@@ -14837,7 +14837,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by ModemManager $as_me 0.4.997, which was
+This file was extended by ModemManager $as_me 0.5, which was
generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -14903,7 +14903,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-ModemManager config.status 0.4.997
+ModemManager config.status 0.5
configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 8a451ea..66357d5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
AC_PREREQ([2.60])
-AC_INIT([ModemManager],[0.4.997],[dcbw@redhat.com],[ModemManager])
+AC_INIT([ModemManager],[0.5],[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
diff --git a/debian/changelog b/debian/changelog
index 577a4bb..9061232 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,35 @@
+modemmanager (0.5-1) unstable; urgency=low
+
+ * debian/rules: override dh_autoreconf in a nicer way so we don't have to
+ clean up manually afterwards.
+
+ -- Michael Biebl <biebl@debian.org> Sun, 07 Aug 2011 01:47:27 +0200
+
+modemmanager (0.5-0ubuntu1) oneiric; urgency=low
+
+ * New upstream release 0.5.
+ - gsm: send init command twice to make the N900 happy (LP: #765516)
+ - fix sierra modems' sleep mode command (LP: #459052, #738005)
+ * debian/patches/lp700316_usb_blacklist.patch: add extra devices to blacklist
+ of USB devices known to usually be serial dongles or other things MM should
+ not touch. (LP: #700316)
+ * debian/control, debian/rules: add a -dbg package for modemmanager, and
+ override dh_strip accordingly. (LP: #415394)
+ * debian/rules: fix .la/.a file removal to not fail if there is nothing to
+ remove.
+ * debian/modemmanager.install: install files to the modemmanager package
+ explicitly now that it's not the only binary package.
+
+ -- Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> Fri, 05 Aug 2011 12:46:32 -0400
+
+modemmanager (0.4.997-1ubuntu1) oneiric; urgency=low
+
+ * debian/modemmanager.upstart: add an upstart config file so ModemManager
+ gets started just before NM, and stopped along with it. (LP: #806082)
+ * debian/rules: install upstart file only for Ubuntu.
+
+ -- Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> Fri, 08 Jul 2011 15:32:13 -0400
+
modemmanager (0.4.997-1) unstable; urgency=low
* debian/watch: Switch to .bz2 tarballs.
diff --git a/debian/control b/debian/control
index bed079e..ee13168 100644
--- a/debian/control
+++ b/debian/control
@@ -28,3 +28,18 @@ Description: D-Bus service for managing modems
.
Git Repository: http://cgit.freedesktop.org/ModemManager/ModemManager/
+Package: modemmanager-dbg
+Architecture: any
+Section: debug
+Priority: extra
+Depends: modemmanager (= ${binary:Version}),
+ ${misc:Depends}
+Description: D-Bus service for managing modems - debugging symbols
+ Provides a D-Bus interface to communicate with mobile broadband (GSM, CDMA,
+ UMTS, ...) cards. Implements a loadable plugin interface to add work-arounds
+ for non standard devices. Also provides patches to use networkmanager (and
+ the applet) with modem manager.
+ .
+ Git Repository: http://cgit.freedesktop.org/ModemManager/ModemManager/
+ .
+ This package contains debugging symbols for ModemManager.
diff --git a/debian/modemmanager.install b/debian/modemmanager.install
new file mode 100644
index 0000000..9c13e90
--- /dev/null
+++ b/debian/modemmanager.install
@@ -0,0 +1,3 @@
+debian/tmp/etc
+debian/tmp/lib
+debian/tmp/usr
diff --git a/debian/patches/lp700316_usb_blacklist.patch b/debian/patches/lp700316_usb_blacklist.patch
new file mode 100644
index 0000000..0deb577
--- /dev/null
+++ b/debian/patches/lp700316_usb_blacklist.patch
@@ -0,0 +1,19 @@
+Index: modemmanager-0.5/src/77-mm-usb-device-blacklist.rules
+===================================================================
+--- modemmanager-0.5.orig/src/77-mm-usb-device-blacklist.rules 2011-08-05 11:37:37.535618352 -0400
++++ modemmanager-0.5/src/77-mm-usb-device-blacklist.rules 2011-08-05 11:39:06.565883158 -0400
+@@ -68,5 +68,14 @@
+ # ATEN Intl UC-232A (Prolific)
+ ATTRS{idVendor}=="0557", ATTRS{idProduct}=="2008", ENV{ID_MM_DEVICE_IGNORE}="1"
+
++# Prolific Technology, Inc. PL2303 Serial Port
++ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", ENV{ID_MM_DEVICE_IGNORE}="1"
++
++# Cygnal Integrated Products, Inc. CP210x
++ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ENV{ID_MM_DEVICE_IGNORE}="1"
++
++# QinHeng Electronics HL-340
++ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", ENV{ID_MM_DEVICE_IGNORE}="1"
++
+ LABEL="mm_usb_device_blacklist_end"
+
diff --git a/debian/patches/series b/debian/patches/series
index 235bfb9..808f502 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,3 @@
# patches for modemmanager
qdl-blacklist.patch
+lp700316_usb_blacklist.patch
diff --git a/debian/rules b/debian/rules
index d61f645..e906b96 100755
--- a/debian/rules
+++ b/debian/rules
@@ -9,13 +9,16 @@ DH_ALWAYS_EXCLUDE=.svn:.git:.bzr
%:
dh $@ --with autoreconf
+autoreconf:
+ intltoolize -f
+ autoreconf -f -i
+
override_dh_autoreconf:
- intltoolize --force
- dh_autoreconf
+ dh_autoreconf debian/rules -- autoreconf
-override_dh_autoreconf_clean:
- dh_autoreconf_clean
- rm -f intltool-*.in po/Makefile.in.in m4/intltool.m4 || true
+override_dh_auto_clean:
+ rm -f $(CURDIR)/debian/modemmanager.upstart
+ dh_auto_clean
override_dh_auto_configure:
dh_auto_configure -- \
@@ -24,9 +27,15 @@ override_dh_auto_configure:
override_dh_makeshlibs:
dh_makeshlibs -X/usr/lib/ModemManager/ -X/usr/lib/pppd/
+override_dh_strip:
+ dh_strip --dbg-package=modemmanager-dbg
+
override_dh_install:
- find $(CURDIR)/debian/modemmanager/ -name \*.a | xargs rm
- find $(CURDIR)/debian/modemmanager/ -name \*.la | xargs rm
+ if dpkg-vendor --is ubuntu; then \
+ cp $(CURDIR)/debian/ubuntu/modemmanager.upstart $(CURDIR)/debian; \
+ fi
+ find $(CURDIR)/debian/tmp/ -name \*.a -exec rm {} \;
+ find $(CURDIR)/debian/tmp/ -name \*.la -exec rm {} \;
dh_install
override_dh_installdocs:
diff --git a/debian/ubuntu/modemmanager.upstart b/debian/ubuntu/modemmanager.upstart
new file mode 100644
index 0000000..a7b1642
--- /dev/null
+++ b/debian/ubuntu/modemmanager.upstart
@@ -0,0 +1,14 @@
+# modemmanager - modem manager
+#
+# The Network Manager daemon manages the system's network connections,
+# automatically switching between the best available.
+
+description "modem connection manager"
+
+start on starting network-manager
+stop on stopping network-manager
+
+expect fork
+respawn
+
+exec /usr/sbin/modem-manager &
diff --git a/include/mm-modem.h b/include/mm-modem.h
index 7f1a4a7..4fe025c 100644
--- a/include/mm-modem.h
+++ b/include/mm-modem.h
@@ -36,6 +36,7 @@
*/
#define MM_MANAGER_METHOD_ENUMERATEDEVICES "EnumerateDevices"
+#define MM_MANAGER_METHOD_SETLOGGING "SetLogging"
#define MM_MANAGER_SIGNAL_DEVICEADDED "DeviceAdded"
#define MM_MANAGER_SIGNAL_DEVICEREMOVED "DeviceRemoved"
@@ -158,6 +159,7 @@
#define MM_MODEM_GSM_ACCESS_TECH_HSUPA 7
#define MM_MODEM_GSM_ACCESS_TECH_HSPA 8
#define MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS 9
+#define MM_MODEM_GSM_ACCESS_TECH_LTE 10
/* MM_MODEM_GSM_MODE flag values */
diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml
index 75cdeb0..f3f6900 100644
--- a/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml
+++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml
@@ -163,6 +163,9 @@
<tp:enumvalue suffix="HSPA_PLUS" value="9">
<tp:docstring>HSPA+ (ETSI 27.007: "UTRAN w/HSPA+")</tp:docstring>
</tp:enumvalue>
+ <tp:enumvalue suffix="LTE" value="10">
+ <tp:docstring>LTE (ETSI 27.007: "E-UTRAN")</tp:docstring>
+ </tp:enumvalue>
</tp:enum>
</interface>
diff --git a/introspection/org.freedesktop.ModemManager.Modem.Simple.xml b/introspection/org.freedesktop.ModemManager.Modem.Simple.xml
index bee1017..a86af4f 100644
--- a/introspection/org.freedesktop.ModemManager.Modem.Simple.xml
+++ b/introspection/org.freedesktop.ModemManager.Modem.Simple.xml
@@ -16,11 +16,12 @@
common ones are:
'pin' : string
- 'network_id' : string
+ 'network_id' : string (GSM/HSPA only)
'band' : uint
'network_mode' : uint
- 'apn' : string
+ 'apn' : string (GSM/HSPA only)
'number' : string
+ 'rm-protocol' : uint (CDMA/EVDO only) (1 - Relay, 2 - Network PPP)
</tp:docstring>
</arg>
</method>
diff --git a/introspection/org.freedesktop.ModemManager.xml b/introspection/org.freedesktop.ModemManager.xml
index bdeac01..0bc9fb2 100644
--- a/introspection/org.freedesktop.ModemManager.xml
+++ b/introspection/org.freedesktop.ModemManager.xml
@@ -14,6 +14,18 @@
</arg>
</method>
+ <method name="SetLogging">
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_set_logging"/>
+ <tp:docstring>
+ Set logging verbosity.
+ </tp:docstring>
+ <arg name="level" type="s" direction="in">
+ <tp:docstring>
+ One of [ERR, WARN, INFO, DEBUG].
+ </tp:docstring>
+ </arg>
+ </method>
+
<signal name="DeviceAdded">
<tp:docstring>
A device was added to the system.
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 8eaffb4..47fc3b0 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -375,6 +375,9 @@ libmm_plugin_samsung_la_LDFLAGS = \
-module \
-avoid-version
+libmm_plugin_samsung_la_LIBADD = \
+ $(builddir)/libicera-utils.la
+
udevrulesdir = $(UDEV_BASE_DIR)/rules.d
udevrules_DATA = \
77-mm-ericsson-mbm.rules \
diff --git a/plugins/Makefile.in b/plugins/Makefile.in
index 1c6e440..df6b5e3 100644
--- a/plugins/Makefile.in
+++ b/plugins/Makefile.in
@@ -194,7 +194,7 @@ libmm_plugin_option_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
$(AM_CFLAGS) $(CFLAGS) $(libmm_plugin_option_la_LDFLAGS) \
$(LDFLAGS) -o $@
-libmm_plugin_samsung_la_LIBADD =
+libmm_plugin_samsung_la_DEPENDENCIES = $(builddir)/libicera-utils.la
am_libmm_plugin_samsung_la_OBJECTS = \
libmm_plugin_samsung_la-mm-plugin-samsung.lo \
libmm_plugin_samsung_la-mm-modem-samsung-gsm.lo
@@ -834,6 +834,9 @@ libmm_plugin_samsung_la_LDFLAGS = \
-module \
-avoid-version
+libmm_plugin_samsung_la_LIBADD = \
+ $(builddir)/libicera-utils.la
+
udevrulesdir = $(UDEV_BASE_DIR)/rules.d
udevrules_DATA = \
77-mm-ericsson-mbm.rules \
diff --git a/plugins/mm-modem-gobi-gsm.c b/plugins/mm-modem-gobi-gsm.c
index ab19642..eea13ce 100644
--- a/plugins/mm-modem-gobi-gsm.c
+++ b/plugins/mm-modem-gobi-gsm.c
@@ -24,6 +24,7 @@
#include "mm-callback-info.h"
#include "mm-modem-gsm-card.h"
#include "mm-at-serial-port.h"
+#include "mm-modem-helpers.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -56,6 +57,55 @@ mm_modem_gobi_gsm_new (const char *device,
/*****************************************************************************/
static void
+get_act_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ const char *p;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else {
+ p = mm_strip_tag (response->str, "*CNTI:");
+ p = strchr (p, ',');
+ if (p)
+ act = mm_gsm_string_to_access_tech (p + 1);
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL);
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_access_technology (MMGenericGsm *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (modem, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "*CNTI=0", 3, get_act_request_done, info);
+}
+
+/*****************************************************************************/
+
+static void
get_string_done (MMAtSerialPort *port,
GString *response,
GError *error,
@@ -118,5 +168,8 @@ mm_modem_gobi_gsm_init (MMModemGobiGsm *self)
static void
mm_modem_gobi_gsm_class_init (MMModemGobiGsmClass *klass)
{
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
+
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c
index e038069..5f4c2fb 100644
--- a/plugins/mm-modem-huawei-gsm.c
+++ b/plugins/mm-modem-huawei-gsm.c
@@ -25,21 +25,25 @@
#include "mm-modem-huawei-gsm.h"
#include "mm-modem-gsm-network.h"
#include "mm-modem-gsm-card.h"
+#include "mm-modem-gsm-ussd.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-at-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-log.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_gsm_card_init (MMModemGsmCard *gsm_card_class);
+static void modem_gsm_ussd_init (MMModemGsmUssd *ussd_class);
G_DEFINE_TYPE_EXTENDED (MMModemHuaweiGsm, mm_modem_huawei_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)
- G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init))
-
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_USSD, modem_gsm_ussd_init)
+)
#define MM_MODEM_HUAWEI_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmPrivate))
@@ -874,6 +878,42 @@ out:
return !!port;
}
+/* Encode to packed GSM - this is what Huawei supports on all known models */
+static char*
+ussd_encode (MMModemGsmUssd *self, const char* command, guint *scheme)
+{
+ char *hex;
+ guint8 *gsm, *packed;
+ guint32 len = 0, packed_len = 0;
+
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
+ gsm = mm_charset_utf8_to_unpacked_gsm (command, &len);
+ packed = gsm_pack (gsm, len, 0, &packed_len);
+ hex = utils_bin2hexstr (packed, packed_len);
+ g_free (packed);
+ g_free (gsm);
+
+ return hex;
+}
+
+/* Unparse packed gsm to utf8 */
+static char*
+ussd_decode (MMModemGsmUssd *self, const char* reply, guint scheme)
+{
+ char *bin, *utf8;
+ guint8 *unpacked;
+ gsize bin_len;
+ guint32 unpacked_len;
+
+ bin = utils_hexstr2bin (reply, &bin_len);
+ unpacked = gsm_unpack ((guint8*)bin, bin_len, 0, &unpacked_len);
+ utf8 = (char*)mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
+
+ g_free (bin);
+ g_free (unpacked);
+ return utf8;
+}
+
/*****************************************************************************/
static void
@@ -902,6 +942,13 @@ mm_modem_huawei_gsm_init (MMModemHuaweiGsm *self)
}
static void
+modem_gsm_ussd_init (MMModemGsmUssd *ussd_class)
+{
+ ussd_class->encode = ussd_encode;
+ ussd_class->decode = ussd_decode;
+}
+
+static void
mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
diff --git a/plugins/mm-modem-icera.c b/plugins/mm-modem-icera.c
index b093b34..8933142 100644
--- a/plugins/mm-modem-icera.c
+++ b/plugins/mm-modem-icera.c
@@ -192,11 +192,13 @@ nwstate_to_act (const char *str)
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"))
+ else if (!strcmp (str, "R99"))
+ return MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ else if (!strcmp (str, "3G-HSDPA") || !strcmp (str, "HSDPA"))
return MM_MODEM_GSM_ACCESS_TECH_HSDPA;
- else if (!strcmp (str, "3G-HSUPA"))
+ else if (!strcmp (str, "3G-HSUPA") || !strcmp (str, "HSUPA"))
return MM_MODEM_GSM_ACCESS_TECH_HSUPA;
- else if (!strcmp (str, "3G-HSDPA-HSUPA"))
+ else if (!strcmp (str, "3G-HSDPA-HSUPA") || !strcmp (str, "HSDPA-HSUPA"))
return MM_MODEM_GSM_ACCESS_TECH_HSPA;
return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
@@ -219,7 +221,15 @@ nwstate_changed (MMAtSerialPort *port,
g_free (str);
}
- str = g_match_info_fetch (info, 3);
+ /* Check the <connection state> field first for the connected access
+ * technology, otherwise if not connected (ie, "-") use the available
+ * access technology from the <tech> field.
+ */
+ str = g_match_info_fetch (info, 4);
+ if (!str || (strcmp (str, "-") == 0)) {
+ g_free (str);
+ str = g_match_info_fetch (info, 3);
+ }
if (str) {
act = nwstate_to_act (str);
g_free (str);
@@ -357,13 +367,48 @@ icera_disconnect_done (MMModem *modem,
}
static void
+query_network_error_code_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMModemIcera *self = MM_MODEM_ICERA (user_data);
+ MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self);
+ MMCallbackInfo *info = priv->connect_pending_data;
+ int nw_activation_err;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ if ((error == NULL) && g_str_has_prefix (response->str, "%IER: ")) {
+ if (sscanf (response->str + 6, "%*d,%*d,%d", &nw_activation_err)) {
+ /* 3GPP TS 24.008 Annex G error codes:
+ * 27 - Unknown or missing access point name
+ * 33 - Requested service option not subscribed
+ */
+ if (nw_activation_err == 27 || nw_activation_err == 33)
+ info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED);
+ }
+ }
+
+ if (info->error == NULL) {
+ /* Generic error since parsing the specific one didn't work */
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Call setup failed");
+ }
+ connect_pending_done (self);
+}
+
+static void
connection_enabled (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemIcera *self = MM_MODEM_ICERA (user_data);
- MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self);
- MMCallbackInfo *info = priv->connect_pending_data;
+ MMAtSerialPort *primary;
char *str;
int status, cid, tmp;
@@ -400,12 +445,11 @@ connection_enabled (MMAtSerialPort *port,
break;
case 3:
/* Call setup failure? */
- if (info) {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Call setup failed");
- }
- connect_pending_done (self);
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM(self), MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
+ /* Get additional error details */
+ mm_at_serial_port_queue_command (primary, "AT%IER?", 3,
+ query_network_error_code_done, self);
break;
default:
mm_warn ("Unknown Icera connect status %d", status);
@@ -717,7 +761,11 @@ mm_modem_icera_register_unsolicted_handlers (MMModemIcera *self,
{
GRegex *regex;
- /* %NWSTATE: <rssi>,<mccmnc>,<tech>,<connected>,<regulation> */
+ /* %NWSTATE: <rssi>,<mccmnc>,<tech>,<connection state>,<regulation>
+ *
+ * <connection state> shows the actual access technology in-use when a
+ * PS connection is active.
+ */
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);
@@ -848,4 +896,3 @@ mm_modem_icera_get_type (void)
return icera_type;
}
-
diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c
index 70faef4..7aa8a01 100644
--- a/plugins/mm-modem-mbm.c
+++ b/plugins/mm-modem-mbm.c
@@ -437,18 +437,28 @@ mbm_emrdy_done (MMAtSerialPort *port,
gpointer user_data)
{
MMCallbackInfo *info = user_data;
+ MMModemMbmPrivate *priv;
/* If the modem has already been removed, return without
* scheduling callback */
if (mm_callback_info_check_modem_removed (info))
return;
- if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
- mm_warn ("timed out waiting for EMRDY response.");
- else {
- MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem);
-
- priv->have_emrdy = TRUE;
+ /* EMRDY unsolicited response might have happened between the command
+ * submission and the response. This was seen once:
+ *
+ * (ttyACM0): --> 'AT*EMRDY?<CR>'
+ * (ttyACM0): <-- 'T*EMRD<CR><LF>*EMRDY: 1<CR><LF>Y?'
+ *
+ * So suppress the warning if the unsolicited handler handled the response
+ * before we get here.
+ */
+ priv = MM_MODEM_MBM_GET_PRIVATE (info->modem);
+ if (!priv->have_emrdy) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
+ mm_warn ("timed out waiting for EMRDY response.");
+ else
+ priv->have_emrdy = TRUE;
}
do_init (port, info);
@@ -966,6 +976,13 @@ grab_port (MMModem *modem,
if (port && MM_IS_AT_SERIAL_PORT (port)) {
GRegex *regex;
+ /* The Ericsson modems always have a free AT command port, so we
+ * don't need to flash the ports when disconnecting to get back to
+ * command mode. F5521gw R2A07 resets port properties like echo when
+ * flashed, leading to confusion. bgo #650740
+ */
+ g_object_set (G_OBJECT (port), MM_SERIAL_PORT_FLASH_OK, FALSE, NULL);
+
if (ptype == MM_PORT_TYPE_PRIMARY) {
regex = g_regex_new ("\\r\\n\\*E2NAP: (\\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, mbm_e2nap_received, modem, NULL);
diff --git a/plugins/mm-modem-nokia.c b/plugins/mm-modem-nokia.c
index 56b8c91..56f4c1b 100644
--- a/plugins/mm-modem-nokia.c
+++ b/plugins/mm-modem-nokia.c
@@ -58,7 +58,6 @@ grab_port (MMModem *modem,
MMGenericGsm *gsm = MM_GENERIC_GSM (modem);
MMPortType ptype = MM_PORT_TYPE_IGNORED;
MMPort *port = NULL;
- gulong send_delay = 5000;
if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
@@ -76,9 +75,6 @@ grab_port (MMModem *modem,
mm_serial_parser_v1_e1_destroy);
}
- /* N900 appears to need longer delay between port bytes */
- g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, send_delay, NULL);
-
return !!port;
}
diff --git a/plugins/mm-modem-option-utils.c b/plugins/mm-modem-option-utils.c
index 61ca5d1..2316ee4 100644
--- a/plugins/mm-modem-option-utils.c
+++ b/plugins/mm-modem-option-utils.c
@@ -190,6 +190,22 @@ owcti_to_mm (char owcti, MMModemGsmAccessTech *out_act)
}
static gboolean
+ossys_to_mm (char ossys, MMModemGsmAccessTech *out_act)
+{
+ if (ossys == '0') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ return TRUE;
+ } else if (ossys == '2') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ return TRUE;
+ } else if (ossys == '3') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
parse_octi_response (GString *response, MMModemGsmAccessTech *act)
{
MMModemGsmAccessTech cur_act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
@@ -222,6 +238,39 @@ parse_octi_response (GString *response, MMModemGsmAccessTech *act)
return success;
}
+static gboolean
+parse_ossys_response (GString *response, MMModemGsmAccessTech *act)
+{
+ MMModemGsmAccessTech cur_act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ const char *p;
+ GRegex *r;
+ GMatchInfo *match_info;
+ char *str;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (act != NULL, FALSE);
+ g_return_val_if_fail (response != NULL, FALSE);
+
+ p = mm_strip_tag (response->str, "_OSSYS:");
+
+ r = g_regex_new ("(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL);
+ g_return_val_if_fail (r != NULL, FALSE);
+
+ g_regex_match (r, p, 0, &match_info);
+ if (g_match_info_matches (match_info)) {
+ str = g_match_info_fetch (match_info, 2);
+ if (str && ossys_to_mm (str[0], &cur_act)) {
+ *act = cur_act;
+ success = TRUE;
+ }
+ g_free (str);
+ }
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ return success;
+}
+
static void
ossys_octi_request_done (MMAtSerialPort *port,
GString *response,
@@ -261,18 +310,8 @@ option_ossys_tech_changed (MMAtSerialPort *port,
char *str;
str = g_match_info_fetch (info, 1);
- if (str) {
- switch (atoi (str)) {
- case 0:
- act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
- break;
- case 2:
- act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
- break;
- default:
- break;
- }
- }
+ if (str)
+ ossys_to_mm (str[0], &act);
g_free (str);
mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
@@ -408,7 +447,7 @@ get_act_octi_request_done (MMAtSerialPort *port,
{
MMCallbackInfo *info = user_data;
MMModemGsmAccessTech octi = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
- MMModemGsmAccessTech owcti;
+ MMModemGsmAccessTech act;
/* If the modem has already been removed, return without
* scheduling callback */
@@ -417,9 +456,11 @@ get_act_octi_request_done (MMAtSerialPort *port,
if (!error) {
if (parse_octi_response (response, &octi)) {
- /* If no 3G tech yet or current tech isn't 3G, then 2G tech is the best */
- owcti = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "owcti"));
- if (octi && !owcti)
+ /* If current tech is 2G or unknown then use the more specific
+ * OCTI response.
+ */
+ act = GPOINTER_TO_UINT (mm_callback_info_get_result (info));
+ if (act < MM_MODEM_GSM_ACCESS_TECH_UMTS)
mm_callback_info_set_result (info, GUINT_TO_POINTER (octi), NULL);
}
}
@@ -444,13 +485,57 @@ get_act_owcti_request_done (MMAtSerialPort *port,
if (!error) {
p = mm_strip_tag (response->str, "_OWCTI:");
- if (owcti_to_mm (*p, &owcti)) {
- /* 3G tech always takes precedence over 2G tech */
- if (owcti)
- mm_callback_info_set_result (info, GUINT_TO_POINTER (owcti), NULL);
+ if (owcti_to_mm (*p, &owcti) && owcti)
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (owcti), NULL);
+ }
+
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+get_act_ossys_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemGsmAccessTech ossys = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ gboolean check_2g = TRUE, check_3g = TRUE;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ /* If for some reason the OSSYS request failed, still try to check
+ * explicit 2G/3G mode with OCTI and OWCTI; maybe we'll get something.
+ */
+
+ if (!error) {
+ /* Response is _OSSYS: <n>,<act> so we must skip the <n> */
+ if (parse_ossys_response (response, &ossys)) {
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (ossys), NULL);
+
+ /* If the OSSYS response indicated a generic access tech type
+ * then only check for more specific access tech of that type.
+ */
+ if (ossys == MM_MODEM_GSM_ACCESS_TECH_GPRS)
+ check_3g = FALSE;
+ if (ossys == MM_MODEM_GSM_ACCESS_TECH_UMTS)
+ check_2g = FALSE;
}
}
+ if (check_2g)
+ mm_at_serial_port_queue_command (port, "_OCTI?", 3, get_act_octi_request_done, info);
+ else
+ mm_callback_info_chain_complete_one (info); /* complete it if it wasn't used */
+
+ if (check_3g)
+ mm_at_serial_port_queue_command (port, "_OWCTI?", 3, get_act_owcti_request_done, info);
+ else
+ mm_callback_info_chain_complete_one (info); /* complete it if it wasn't used */
+
mm_callback_info_chain_complete_one (info);
}
@@ -463,7 +548,7 @@ option_get_access_technology (MMGenericGsm *modem,
MMCallbackInfo *info;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_callback_info_chain_start (info, 2);
+ mm_callback_info_chain_start (info, 3);
port = mm_generic_gsm_get_best_at_port (modem, &info->error);
if (!port) {
@@ -471,7 +556,6 @@ option_get_access_technology (MMGenericGsm *modem,
return;
}
- mm_at_serial_port_queue_command (port, "_OCTI?", 3, get_act_octi_request_done, info);
- mm_at_serial_port_queue_command (port, "_OWCTI?", 3, get_act_owcti_request_done, info);
+ mm_at_serial_port_queue_command (port, "_OSSYS?", 3, get_act_ossys_request_done, info);
}
diff --git a/plugins/mm-modem-samsung-gsm.c b/plugins/mm-modem-samsung-gsm.c
index d873653..f2d339b 100755
--- a/plugins/mm-modem-samsung-gsm.c
+++ b/plugins/mm-modem-samsung-gsm.c
@@ -478,8 +478,12 @@ disable (MMModem *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);
+ /*
+ * Command to ensure unsolicited message disable completes.
+ * Turns the radios off, which seems like a reasonable
+ * think to do when disabling.
+ */
+ mm_at_serial_port_queue_command (primary, "AT+CFUN=4", 5, disable_unsolicited_done, info);
}
static void
diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c
index 6d4e4d5..551142e 100644
--- a/plugins/mm-modem-sierra-gsm.c
+++ b/plugins/mm-modem-sierra-gsm.c
@@ -580,7 +580,7 @@ clear_user_pass (MMModemSierraGsm *self)
g_free (priv->username);
priv->username = NULL;
g_free (priv->password);
- priv->username = NULL;
+ priv->password = NULL;
}
static void
diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c
index 0f69328..6c9f395 100644
--- a/plugins/mm-modem-zte.c
+++ b/plugins/mm-modem-zte.c
@@ -26,15 +26,19 @@
#include "mm-modem-helpers.h"
#include "mm-modem-simple.h"
#include "mm-modem-icera.h"
+#include "mm-modem-gsm-ussd.h"
static void modem_init (MMModem *modem_class);
static void modem_icera_init (MMModemIcera *icera_class);
static void modem_simple_init (MMModemSimple *simple_class);
+static void modem_gsm_ussd_init (MMModemGsmUssd *ussd_class);
G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_ICERA, modem_icera_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init))
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_USSD, modem_gsm_ussd_init)
+)
#define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate))
@@ -683,6 +687,19 @@ get_icera_private (MMModemIcera *icera)
/*****************************************************************************/
+static char*
+ussd_encode (MMModemGsmUssd *self, const char* command, guint *scheme)
+{
+ char *cmd;
+
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
+ cmd = g_strdup (command);
+
+ return cmd;
+}
+
+/*****************************************************************************/
+
static void
modem_init (MMModem *modem_class)
{
@@ -710,6 +727,12 @@ mm_modem_zte_init (MMModemZte *self)
}
static void
+modem_gsm_ussd_init (MMModemGsmUssd *ussd_class)
+{
+ ussd_class->encode = ussd_encode;
+}
+
+static void
dispose (GObject *object)
{
MMModemZte *self = MM_MODEM_ZTE (object);
diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c
index a819b4e..de777cc 100644
--- a/plugins/mm-plugin-option.c
+++ b/plugins/mm-plugin-option.c
@@ -59,7 +59,7 @@ supports_port (MMPluginBase *base,
GUdevDevice *port;
guint32 cached = 0, level;
const char *driver, *subsys, *name;
- guint16 vendor = 0;
+ guint16 vendor = 0, product = 0;
/* Can't do anything with non-serial ports */
port = mm_plugin_base_supports_task_get_port (task);
@@ -70,13 +70,14 @@ supports_port (MMPluginBase *base,
name = g_udev_device_get_name (port);
driver = mm_plugin_base_supports_task_get_driver (task);
- if (!driver || (strcmp (driver, "option1") && strcmp (driver, "option")))
+ if (!driver || (strcmp (driver, "option1") && strcmp (driver, "option") && strcmp (driver, "nozomi")))
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL))
+ if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product))
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- if (vendor != 0x0af0)
+ if ( (vendor != 0x0af0) /* Option USB devices */
+ && (vendor != 0x1931 || product != 0x000c)) /* Nozomi CardBus devices */
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) {
diff --git a/plugins/mm-plugin-samsung.c b/plugins/mm-plugin-samsung.c
index 350d4de..9ea2051 100755
--- a/plugins/mm-plugin-samsung.c
+++ b/plugins/mm-plugin-samsung.c
@@ -84,7 +84,7 @@ supports_port (MMPluginBase *base,
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
/* Product ID check */
- if (product != 0x6872)
+ if (product != 0x6872 && product != 0x6906)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
/* The ethernet ports are obviously supported and don't need probing */
diff --git a/src/Makefile.am b/src/Makefile.am
index e813e7e..71008c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,9 @@ libmodem_helpers_la_SOURCES = \
mm-charsets.c \
mm-charsets.h \
mm-utils.c \
- mm-utils.h
+ mm-utils.h \
+ mm-sms-utils.c \
+ mm-sms-utils.h
libserial_la_CPPFLAGS = \
$(MM_CFLAGS) \
diff --git a/src/Makefile.in b/src/Makefile.in
index 8413fd1..1f2f622 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -64,7 +64,8 @@ libmodem_helpers_la_LIBADD =
am_libmodem_helpers_la_OBJECTS = libmodem_helpers_la-mm-errors.lo \
libmodem_helpers_la-mm-modem-helpers.lo \
libmodem_helpers_la-mm-charsets.lo \
- libmodem_helpers_la-mm-utils.lo
+ libmodem_helpers_la-mm-utils.lo \
+ libmodem_helpers_la-mm-sms-utils.lo
libmodem_helpers_la_OBJECTS = $(am_libmodem_helpers_la_OBJECTS)
AM_V_lt = $(am__v_lt_$(V))
am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
@@ -394,7 +395,9 @@ libmodem_helpers_la_SOURCES = \
mm-charsets.c \
mm-charsets.h \
mm-utils.c \
- mm-utils.h
+ mm-utils.h \
+ mm-sms-utils.c \
+ mm-sms-utils.h
libserial_la_CPPFLAGS = \
$(MM_CFLAGS) \
@@ -542,6 +545,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-charsets.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-errors.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-modem-helpers.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-sms-utils.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-utils.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libserial_la-mm-at-serial-port.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libserial_la-mm-port.Plo@am__quote@
@@ -631,6 +635,14 @@ libmodem_helpers_la-mm-utils.lo: mm-utils.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_helpers_la-mm-utils.lo `test -f 'mm-utils.c' || echo '$(srcdir)/'`mm-utils.c
+libmodem_helpers_la-mm-sms-utils.lo: mm-sms-utils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_helpers_la-mm-sms-utils.lo -MD -MP -MF $(DEPDIR)/libmodem_helpers_la-mm-sms-utils.Tpo -c -o libmodem_helpers_la-mm-sms-utils.lo `test -f 'mm-sms-utils.c' || echo '$(srcdir)/'`mm-sms-utils.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmodem_helpers_la-mm-sms-utils.Tpo $(DEPDIR)/libmodem_helpers_la-mm-sms-utils.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mm-sms-utils.c' object='libmodem_helpers_la-mm-sms-utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_helpers_la-mm-sms-utils.lo `test -f 'mm-sms-utils.c' || echo '$(srcdir)/'`mm-sms-utils.c
+
libserial_la-mm-port.lo: mm-port.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libserial_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libserial_la-mm-port.lo -MD -MP -MF $(DEPDIR)/libserial_la-mm-port.Tpo -c -o libserial_la-mm-port.lo `test -f 'mm-port.c' || echo '$(srcdir)/'`mm-port.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libserial_la-mm-port.Tpo $(DEPDIR)/libserial_la-mm-port.Plo
diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c
index a230e69..302a816 100644
--- a/src/mm-callback-info.c
+++ b/src/mm-callback-info.c
@@ -151,6 +151,14 @@ mm_callback_info_string_new (MMModem *modem,
return mm_callback_info_new_full (modem, invoke_mm_modem_string_fn, (GCallback) callback, user_data);
}
+gpointer
+mm_callback_info_get_result (MMCallbackInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return mm_callback_info_get_data (info, CALLBACK_INFO_RESULT);
+}
+
void
mm_callback_info_set_result (MMCallbackInfo *info,
gpointer data,
diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h
index 42d9908..a00181c 100644
--- a/src/mm-callback-info.h
+++ b/src/mm-callback-info.h
@@ -58,6 +58,7 @@ MMCallbackInfo *mm_callback_info_string_new (MMModem *modem,
gpointer user_data);
void mm_callback_info_schedule (MMCallbackInfo *info);
+gpointer mm_callback_info_get_result (MMCallbackInfo *info);
void mm_callback_info_set_result (MMCallbackInfo *info,
gpointer data,
GDestroyNotify destroy);
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
index 378555b..11987a2 100644
--- a/src/mm-generic-cdma.c
+++ b/src/mm-generic-cdma.c
@@ -34,6 +34,15 @@
#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state"
+typedef enum {
+ RM_PROTO_ASYNC = 0,
+ RM_PROTO_RELAY = 1,
+ RM_PROTO_NETWORK_PPP = 2,
+ RM_PROTO_NETWORK_SLIP = 3,
+ RM_PROTO_STU_III = 4
+} RmProtocol;
+
+
static void simple_reg_callback (MMModemCdma *modem,
MMModemCdmaRegistrationState cdma_1x_reg_state,
MMModemCdmaRegistrationState evdo_reg_state,
@@ -67,6 +76,10 @@ typedef struct {
gboolean has_spservice;
gboolean has_speri;
+ /* Original and current Rm interface protocol */
+ RmProtocol orig_crm;
+ RmProtocol cur_crm;
+
guint poll_id;
char *meid;
@@ -563,6 +576,29 @@ speri_done (MMAtSerialPort *port,
}
static void
+crm_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ const char *p;
+ unsigned long num;
+
+ if (error)
+ return;
+
+ p = mm_strip_tag (response->str, "+CRM:");
+ if (p) {
+ errno = 0;
+ num = strtoul (p, NULL, 10);
+ if (num >= 0 && num <= 4 && (errno == 0)) {
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->orig_crm = (guint32) num;
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->cur_crm = (guint32) num;
+ }
+ }
+}
+
+static void
enable_all_done (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = user_data;
@@ -599,6 +635,9 @@ enable_all_done (MMModem *modem, GError *error, gpointer user_data)
/* Check for support of Sprint-specific phone commands */
mm_at_serial_port_queue_command (priv->primary, "+SPSERVICE?", 3, spservice_done, self);
mm_at_serial_port_queue_command (priv->primary, "$SPERI?", 3, speri_done, self);
+
+ /* Grab default CRM */
+ mm_at_serial_port_queue_command (priv->primary, "+CRM?", 3, crm_done, self);
}
out:
@@ -1661,11 +1700,17 @@ real_query_registration_state (MMGenericCdma *self,
/* Try Sprint-specific commands */
mm_at_serial_port_queue_command (port, "+SPSERVICE?", 3, reg_query_spservice_done, info);
} else {
- /* Assume we're registered on the 1x network if we passed +CAD, +CSS,
- * and QCDM Call Manager checking.
+ /* Assume we're at least registered on the 1x network if we passed
+ * +CAD, +CSS, and QCDM Call Manager checking. But don't override a
+ * more specific registration state passed from a caller.
+ */
+ if (cur_cdma_state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+
+ /* Don't touch EVDO state; it's already either UNKNOWN, or been set
+ * by generic checking earlier.
*/
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+
mm_callback_info_schedule (info);
}
}
@@ -1960,12 +2005,63 @@ get_registration_state (MMModemCdma *modem,
}
/*****************************************************************************/
+
+static void
+set_rm_proto_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (mm_callback_info_check_modem_removed (info) == FALSE) {
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+mm_generic_cdma_set_rm_protocol (MMGenericCdma *self,
+ RmProtocol proto,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *cmd;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ port = mm_generic_cdma_get_best_at_port (self, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ g_clear_error (&info->error);
+
+ if (proto < RM_PROTO_ASYNC || proto > RM_PROTO_STU_III) {
+ g_set_error (&info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid Rm interface protocol %d",
+ proto);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ cmd = g_strdup_printf ("+CRM=%d", proto);
+ mm_at_serial_port_queue_command (port, cmd, 3, set_rm_proto_done, info);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
/* MMModemSimple interface */
typedef enum {
SIMPLE_STATE_BEGIN = 0,
SIMPLE_STATE_ENABLE,
SIMPLE_STATE_REGISTER,
+ SIMPLE_STATE_PRE_CONNECT,
SIMPLE_STATE_CONNECT,
SIMPLE_STATE_DONE
} SimpleState;
@@ -1991,6 +2087,32 @@ simple_get_string_property (MMCallbackInfo *info, const char *name, GError **err
}
static gboolean
+simple_get_uint_property (MMCallbackInfo *info,
+ const char *name,
+ guint32 *out_val,
+ GError **error)
+{
+ GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
+ GValue *value;
+
+ g_return_val_if_fail (out_val != NULL, FALSE);
+
+ value = (GValue *) g_hash_table_lookup (properties, name);
+ if (value) {
+ if (G_VALUE_HOLDS_UINT (value)) {
+ *out_val = g_value_get_uint (value);
+ return TRUE;
+ }
+
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid property type for '%s': %s (uint expected)",
+ name, G_VALUE_TYPE_NAME (value));
+ }
+
+ return FALSE;
+}
+
+static gboolean
simple_reg_retry (gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
@@ -2083,10 +2205,11 @@ static void
simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+ MMGenericCdma *self;
+ MMGenericCdmaPrivate *priv;
SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
const char *str;
- guint id;
+ guint id, rm_protocol = 0;
/* Do nothing if modem removed */
if (!modem || mm_callback_info_check_modem_removed (info))
@@ -2097,12 +2220,17 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
goto out;
}
+ self = MM_GENERIC_CDMA (info->modem);
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
switch (state) {
case SIMPLE_STATE_BEGIN:
+ /* Enable state */
state = set_simple_state (info, SIMPLE_STATE_ENABLE);
mm_modem_enable (modem, simple_state_machine, info);
break;
case SIMPLE_STATE_ENABLE:
+ /* Register state */
state = set_simple_state (info, SIMPLE_STATE_REGISTER);
mm_modem_cdma_get_registration_state (MM_MODEM_CDMA (modem),
simple_reg_callback,
@@ -2114,14 +2242,34 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
priv->reg_state_changed_id = id;
break;
case SIMPLE_STATE_REGISTER:
+ /* Pre Connect state */
registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0);
- state = set_simple_state (info, SIMPLE_STATE_CONNECT);
+ state = set_simple_state (info, SIMPLE_STATE_PRE_CONNECT);
mm_modem_set_state (modem, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_REASON_NONE);
+ /* Change the Rm interface protocol due to manager request if needed */
+ if (simple_get_uint_property (info, "rm-protocol", &rm_protocol, &info->error)) {
+ mm_generic_cdma_set_rm_protocol (self, rm_protocol, simple_state_machine, info);
+ break;
+ }
+
+ /* Or if the Rm protocol isn't the default, and there was no request
+ * to change it, do that now.
+ */
+ if (priv->cur_crm != priv->orig_crm) {
+ mm_generic_cdma_set_rm_protocol (self, priv->orig_crm, simple_state_machine, info);
+ break;
+ }
+
+ /* Fall through */
+ case SIMPLE_STATE_PRE_CONNECT:
+ /* Connect state */
+ state = set_simple_state (info, SIMPLE_STATE_CONNECT);
str = simple_get_string_property (info, "number", &info->error);
mm_modem_connect (modem, str, simple_state_machine, info);
break;
case SIMPLE_STATE_CONNECT:
+ /* All done! */
state = set_simple_state (info, SIMPLE_STATE_DONE);
break;
case SIMPLE_STATE_DONE:
@@ -2306,10 +2454,15 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_cdma_init (MMGenericCdma *self)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
g_signal_connect (self, "notify::" MM_MODEM_VALID,
G_CALLBACK (modem_valid_changed), NULL);
g_signal_connect (self, "notify::" MM_MODEM_STATE,
G_CALLBACK (modem_state_changed), NULL);
+
+ /* Default to Network Layer Rm interface/PPP */
+ priv->orig_crm = priv->cur_crm = RM_PROTO_NETWORK_PPP;
}
static void
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 0b7ea01..cee1bd6 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -37,6 +37,7 @@
#include "mm-properties-changed-signal.h"
#include "mm-utils.h"
#include "mm-modem-location.h"
+#include "mm-sms-utils.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -120,7 +121,11 @@ typedef struct {
gboolean loc_enabled;
gboolean loc_signal;
+ gboolean ussd_enabled;
+ MMCallbackInfo *pending_ussd_info;
MMModemGsmUssdState ussd_state;
+ char *ussd_network_request;
+ char *ussd_network_notification;
/* SMS */
GHashTable *sms_present;
@@ -174,10 +179,13 @@ static void cmti_received (MMAtSerialPort *port,
GMatchInfo *info,
gpointer user_data);
+static void cusd_received (MMAtSerialPort *port,
+ GMatchInfo *info,
+ gpointer user_data);
+
#define GS_HASH_TAG "get-sms"
static GValue *simple_string_value (const char *str);
static GValue *simple_uint_value (guint32 i);
-static GValue *simple_boolean_value (gboolean b);
static void simple_free_gvalue (gpointer data);
MMModem *
@@ -838,11 +846,16 @@ 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);
+ g_regex_unref (regex);
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);
+ regex = g_regex_new ("\\r\\n\\+CUSD:\\s*(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, cusd_received, self, NULL);
+ g_regex_unref (regex);
+
if (ptype == MM_PORT_TYPE_PRIMARY) {
priv->primary = MM_AT_SERIAL_PORT (port);
if (!priv->data) {
@@ -1400,6 +1413,21 @@ cind_cb (MMAtSerialPort *port,
}
}
+static void
+cusd_enable_cb (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ if (error) {
+ mm_warn ("(%s): failed to enable USSD notifications.",
+ mm_port_get_device (MM_PORT (port)));
+ return;
+ }
+
+ MM_GENERIC_GSM_GET_PRIVATE (user_data)->ussd_enabled = TRUE;
+}
+
void
mm_generic_gsm_enable_complete (MMGenericGsm *self,
GError *error,
@@ -1438,11 +1466,15 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self,
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);
+ /* Enable SMS notifications */
+ mm_at_serial_port_queue_command (priv->primary, "+CNMI=2,1,2,1,0", 3, NULL, NULL);
+
+ /* Enable USSD notifications */
+ mm_at_serial_port_queue_command (priv->primary, "+CUSD=1", 3, cusd_enable_cb, self);
+
mm_at_serial_port_queue_command (priv->primary, "+CIND=?", 3, cind_cb, self);
/* Try one more time to get the SIM ID */
@@ -1543,7 +1575,13 @@ enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
return;
}
+ /* Send the init command twice; some devices (Nokia N900) appear to take a
+ * few commands before responding correctly. Instead of penalizing them for
+ * being stupid the first time by failing to enable the device, just
+ * try again.
+ */
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD, &cmd, NULL);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 3, NULL, NULL);
mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 3, init_done, user_data);
g_free (cmd);
}
@@ -1672,6 +1710,11 @@ disable_flash_done (MMSerialPort *port,
mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CREG=0", 3, NULL, NULL);
mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CGREG=0", 3, NULL, NULL);
+ if (priv->ussd_enabled) {
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CUSD=0", 3, NULL, NULL);
+ priv->ussd_enabled = FALSE;
+ }
+
if (priv->cmer_enabled) {
mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CMER=0", 3, NULL, NULL);
@@ -1715,6 +1758,8 @@ disable (MMModem *modem,
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ mm_generic_gsm_ussd_cleanup (MM_GENERIC_GSM (modem));
+
if (priv->poll_id) {
g_source_remove (priv->poll_id);
priv->poll_id = 0;
@@ -1873,8 +1918,8 @@ get_operator_id_imsi_done (MMModem *modem,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMAtSerialPort *port;
if (error) {
info->error = g_error_copy (error);
@@ -1882,10 +1927,17 @@ get_operator_id_imsi_done (MMModem *modem,
return;
}
+ g_clear_error (&info->error);
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
mm_callback_info_set_data (info, "imsi", g_strdup (result), g_free);
/* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
- mm_at_serial_port_queue_command_cached (priv->primary,
+ mm_at_serial_port_queue_command_cached (port,
"+CRSM=176,28589,0,0,4",
3,
get_mnc_length_done,
@@ -1948,7 +2000,7 @@ get_spn_done (MMAtSerialPort *port,
}
/* Remove the FF filler at the end */
- while (bin[buflen - 1] == (char)0xff)
+ while (buflen > 1 && bin[buflen - 1] == (char)0xff)
buflen--;
/* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
@@ -1972,11 +2024,18 @@ get_imei (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
+ MMAtSerialPort *port;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_at_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port)
+ mm_callback_info_schedule (info);
+ else {
+ g_clear_error (&info->error);
+ mm_at_serial_port_queue_command_cached (port, "+CGSN", 3, get_string_done, info);
+ }
}
static void
@@ -1984,11 +2043,18 @@ get_imsi (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
+ MMAtSerialPort *port;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_at_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port)
+ mm_callback_info_schedule (info);
+ else {
+ g_clear_error (&info->error);
+ mm_at_serial_port_queue_command_cached (port, "+CIMI", 3, get_string_done, info);
+ }
}
static void
@@ -2009,17 +2075,24 @@ get_spn (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
+ MMAtSerialPort *port;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
- mm_at_serial_port_queue_command_cached (priv->primary,
- "+CRSM=176,28486,0,0,17",
- 3,
- get_spn_done,
- info);
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port)
+ mm_callback_info_schedule (info);
+ else {
+ g_clear_error (&info->error);
+
+ /* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
+ mm_at_serial_port_queue_command_cached (port,
+ "+CRSM=176,28486,0,0,17",
+ 3,
+ get_spn_done,
+ info);
+ }
}
static void
@@ -2360,6 +2433,19 @@ reg_info_updated (MMGenericGsm *self,
changed = TRUE;
}
+ /* Don't clear oper code or oper num if at least one of CS or PS state
+ * is home or roaming.
+ */
+ if ( priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING
+ || priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
+ if (update_code && oper_code == NULL)
+ update_code = FALSE;
+ if (update_name && oper_name == NULL)
+ update_name = FALSE;
+ }
+
if (update_code) {
if (g_strcmp0 (oper_code, priv->oper_code) != 0) {
g_free (priv->oper_code);
@@ -3499,7 +3585,12 @@ existing_apns_read (MMAtSerialPort *port,
return;
if (error) {
- info->error = g_error_copy (error);
+ /* Some Android phones don't support querying existing PDP contexts,
+ * but will accept setting the APN. So if CGDCONT? isn't supported,
+ * just ignore that error and hope for the best. (bgo #637327)
+ */
+ if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_NOT_SUPPORTED) == FALSE)
+ info->error = g_error_copy (error);
} else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
GRegex *r;
GMatchInfo *match_info;
@@ -4101,35 +4192,6 @@ 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_NUMBER_TYPE_MASK 0x70
-#define SMS_NUMBER_TYPE_UNKNOWN 0x00
-#define SMS_NUMBER_TYPE_INTL 0x10
-#define SMS_NUMBER_TYPE_ALPHA 0x50
-
-#define SMS_NUMBER_PLAN_MASK 0x0f
-#define SMS_NUMBER_PLAN_TELEPHONE 0x01
-
-#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,
@@ -4180,219 +4242,7 @@ 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 semi-octets */
-static char *
-sms_decode_address (const guint8 *address, int len)
-{
- guint8 addrtype, addrplan;
- char *utf8;
-
- addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
- addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
- address++;
-
- if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
- guint8 *unpacked;
- guint32 unpacked_len;
- unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
- utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
- unpacked_len);
- g_free(unpacked);
- } else if (addrtype == SMS_NUMBER_TYPE_INTL &&
- addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
- /* International telphone number, format as "+1234567890" */
- utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */
- utf8[0] = '+';
- sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2);
- } else {
- /*
- * All non-alphanumeric types and plans are just digits, but
- * don't apply any special formatting if we don't know the
- * format.
- */
- utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */
- 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;
- }
-
- 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,
@@ -4426,11 +4276,8 @@ sms_get_done (MMAtSerialPort *port,
goto out;
}
- properties = sms_parse_pdu (pdu);
+ properties = sms_parse_pdu (pdu, &info->error);
if (!properties) {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Failed to parse SMS PDU");
goto out;
}
@@ -4543,6 +4390,7 @@ sms_list_done (MMAtSerialPort *port,
while (*rstr) {
GHashTable *properties;
+ GError *local;
int idx;
char pdu[SMS_MAX_PDU_LEN + 1];
@@ -4554,11 +4402,14 @@ sms_list_done (MMAtSerialPort *port,
}
rstr += offset;
- properties = sms_parse_pdu (pdu);
+ properties = sms_parse_pdu (pdu, &local);
if (properties) {
g_hash_table_insert (properties, "index",
simple_uint_value (idx));
g_ptr_array_add (results, properties);
+ } else {
+ /* Ignore the error */
+ g_clear_error(&local);
}
}
/*
@@ -4659,93 +4510,207 @@ ussd_update_state (MMGenericGsm *self, MMModemGsmUssdState new_state)
}
}
+void
+mm_generic_gsm_ussd_cleanup (MMGenericGsm *self)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (priv->pending_ussd_info) {
+ /* And schedule the callback */
+ g_clear_error (&priv->pending_ussd_info->error);
+ priv->pending_ussd_info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "USSD session terminated without reply.");
+ mm_callback_info_schedule (priv->pending_ussd_info);
+ priv->pending_ussd_info = NULL;
+ }
+
+ ussd_update_state (self, MM_MODEM_GSM_USSD_STATE_IDLE);
+
+ g_free (priv->ussd_network_request);
+ priv->ussd_network_request = NULL;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_REQUEST);
+
+ g_free (priv->ussd_network_notification);
+ priv->ussd_network_notification = NULL;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION);
+}
+
+static char *
+decode_ussd_response (MMGenericGsm *self,
+ const char *reply,
+ MMModemCharset cur_charset)
+{
+ char **items, **iter, *p;
+ char *str = NULL;
+ gint encoding = -1;
+
+ /* Look for the first ',' */
+ p = strchr (reply, ',');
+ if (p == NULL)
+ return NULL;
+
+ items = g_strsplit_set (p + 1, " ,", -1);
+ for (iter = items; iter && *iter; iter++) {
+ if (*iter[0] == '\0')
+ continue;
+ if (str == NULL)
+ str = *iter;
+ else if (encoding == -1) {
+ encoding = atoi (*iter);
+ mm_dbg ("USSD data coding scheme %d", encoding);
+ break; /* All done */
+ }
+ }
+
+ /* Strip quotes */
+ if (str[0] == '"')
+ str++;
+ p = strchr (str, '"');
+ if (p)
+ *p = '\0';
+
+ return mm_modem_gsm_ussd_decode (MM_MODEM_GSM_USSD (self), str,
+ cur_charset);
+}
+
+static char*
+ussd_encode (MMModemGsmUssd *modem, const char* command, guint *scheme)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ GByteArray *ussd_command = g_byte_array_new();
+ gboolean success;
+ char *hex = NULL;
+
+ /* encode to cur_charset */
+ success = mm_modem_charset_byte_array_append (ussd_command, command, FALSE,
+ priv->cur_charset);
+ g_warn_if_fail (success == TRUE);
+ if (!success)
+ goto out;
+
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
+ /* convert to hex representation */
+ hex = utils_bin2hexstr (ussd_command->data, ussd_command->len);
+
+ out:
+ g_byte_array_free (ussd_command, TRUE);
+ return hex;
+}
+
+static char*
+ussd_decode (MMModemGsmUssd *modem, const char* reply, guint scheme)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ char *converted;
+
+ converted = mm_modem_charset_hex_to_utf8 (reply, priv->cur_charset);
+ return converted;
+}
+
static void
-ussd_send_done (MMAtSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+cusd_received (MMAtSerialPort *port,
+ GMatchInfo *info,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMGenericGsmPrivate *priv;
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ GError *error = NULL;
gint status;
- gboolean parsed = FALSE;
MMModemGsmUssdState ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
- const char *str, *start = NULL, *end = NULL;
char *reply = NULL, *converted;
- /* If the modem has already been removed, return without
- * scheduling callback */
- if (mm_callback_info_check_modem_removed (info))
+ reply = g_match_info_fetch (info, 1);
+ if (!reply || !isdigit (*reply)) {
+ mm_warn ("Recieved invalid USSD response: '%s'", reply ? reply : "(none)");
+ g_free (reply);
return;
-
- if (error) {
- info->error = g_error_copy (error);
- goto done;
}
- priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- ussd_state = priv->ussd_state;
-
- str = mm_strip_tag (response->str, "+CUSD:");
- if (!str || !isdigit (*str))
- goto done;
-
- status = g_ascii_digit_value (*str);
+ status = g_ascii_digit_value (*reply);
switch (status) {
case 0: /* no further action required */
- ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ converted = decode_ussd_response (self, reply, priv->cur_charset);
+ if (priv->pending_ussd_info) {
+ /* Response to the user's request */
+ mm_callback_info_set_result (priv->pending_ussd_info, converted, g_free);
+ } else {
+ /* Network-initiated USSD-Notify */
+ g_free (priv->ussd_network_notification);
+ priv->ussd_network_notification = converted;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION);
+ }
break;
case 1: /* further action required */
ussd_state = MM_MODEM_GSM_USSD_STATE_USER_RESPONSE;
+ converted = decode_ussd_response (self, reply, priv->cur_charset);
+ if (priv->pending_ussd_info) {
+ mm_callback_info_set_result (priv->pending_ussd_info, converted, g_free);
+ } else {
+ /* Network-initiated USSD-Request */
+ g_free (priv->ussd_network_request);
+ priv->ussd_network_request = converted;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_NETWORK_REQUEST);
+ }
break;
case 2:
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "USSD terminated by network.");
- ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "USSD terminated by network.");
break;
case 4:
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Operiation not supported.");
- ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Operation not supported.");
break;
default:
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Unknown USSD reply %d", status);
- ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Unhandled USSD reply %d", status);
break;
}
- if (info->error)
- goto done;
-
- /* look for the reply */
- if ((start = strchr (str, '"')) && (end = strrchr (str, '"')) && (start != end))
- reply = g_strndup (start + 1, end - start -1);
- if (reply) {
- /* look for the reply data coding scheme */
- if ((start = strrchr (end, ',')) != NULL)
- mm_dbg ("USSD data coding scheme %d", atoi (start + 1));
+ ussd_update_state (self, ussd_state);
- converted = mm_modem_charset_hex_to_utf8 (reply, priv->cur_charset);
- mm_callback_info_set_result (info, converted, g_free);
- parsed = TRUE;
- g_free (reply);
+ if (priv->pending_ussd_info) {
+ if (error)
+ priv->pending_ussd_info->error = g_error_copy (error);
+ mm_callback_info_schedule (priv->pending_ussd_info);
+ priv->pending_ussd_info = NULL;
}
-done:
- if (!parsed && !info->error) {
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse USSD reply '%s'",
- response->str);
+ g_clear_error (&error);
+ g_free (reply);
+}
+
+static void
+ussd_send_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+
+ /* If the modem has already been removed, return without
+ * scheduling callback */
+ if (mm_callback_info_check_modem_removed (info))
+ return;
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ if (error) {
+ /* Some immediate error happened when sending the USSD request */
+ info->error = g_error_copy (error);
+ priv->pending_ussd_info = NULL;
+ mm_callback_info_schedule (info);
+
+ ussd_update_state (MM_GENERIC_GSM (info->modem), MM_MODEM_GSM_USSD_STATE_IDLE);
}
- mm_callback_info_schedule (info);
- if (info->modem)
- ussd_update_state (MM_GENERIC_GSM (info->modem), ussd_state);
+ /* Otherwise if no error wait for the response to show up via the
+ * unsolicited response code.
+ */
}
static void
@@ -4757,10 +4722,11 @@ ussd_send (MMModemGsmUssd *modem,
MMCallbackInfo *info;
char *atc_command;
char *hex;
- GByteArray *ussd_command = g_byte_array_new();
+ guint scheme = 0;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMAtSerialPort *port;
- gboolean success;
+
+ g_warn_if_fail (priv->pending_ussd_info == NULL);
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
@@ -4770,14 +4736,19 @@ ussd_send (MMModemGsmUssd *modem,
return;
}
- /* encode to cur_charset */
- success = mm_modem_charset_byte_array_append (ussd_command, command, FALSE, priv->cur_charset);
- g_warn_if_fail (success == TRUE);
+ /* Cache the callback info since the response is an unsolicited one */
+ priv->pending_ussd_info = info;
- /* convert to hex representation */
- hex = utils_bin2hexstr (ussd_command->data, ussd_command->len);
- g_byte_array_free (ussd_command, TRUE);
- atc_command = g_strdup_printf ("+CUSD=1,\"%s\",15", hex);
+ hex = mm_modem_gsm_ussd_encode (MM_MODEM_GSM_USSD (modem), command, &scheme);
+ if (!hex) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to encode USSD command '%s'",
+ command);
+ mm_callback_info_schedule (info);
+ return;
+ }
+ atc_command = g_strdup_printf ("+CUSD=1,\"%s\",%d", hex, scheme);
g_free (hex);
mm_at_serial_port_queue_command (port, atc_command, 10, ussd_send_done, info);
@@ -4795,9 +4766,8 @@ ussd_initiate (MMModemGsmUssd *modem,
MMCallbackInfo *info;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
- info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
-
if (priv->ussd_state != MM_MODEM_GSM_USSD_STATE_IDLE) {
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
info->error = g_error_new (MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"USSD session already active.");
@@ -4816,9 +4786,8 @@ ussd_respond (MMModemGsmUssd *modem,
MMCallbackInfo *info;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
- info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
-
if (priv->ussd_state != MM_MODEM_GSM_USSD_STATE_USER_RESPONSE) {
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
info->error = g_error_new (MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"No active USSD session, cannot respond.");
@@ -5160,18 +5129,6 @@ simple_uint_value (guint32 i)
}
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)
{
GValue *val;
@@ -5526,6 +5483,8 @@ modem_gsm_ussd_init (MMModemGsmUssd *class)
class->initiate = ussd_initiate;
class->respond = ussd_respond;
class->cancel = ussd_cancel;
+ class->encode = ussd_encode;
+ class->decode = ussd_decode;
}
static void
@@ -5719,10 +5678,10 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, ussd_state_to_string (priv->ussd_state));
break;
case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST:
- g_value_set_string (value, "");
+ g_value_set_string (value, priv->ussd_network_request);
break;
case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION:
- g_value_set_string (value, "");
+ g_value_set_string (value, priv->ussd_network_notification);
break;
case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD:
/* By default, try to set XOFF/XON flow control */
@@ -5740,6 +5699,7 @@ finalize (GObject *object)
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object);
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (object));
+ mm_generic_gsm_ussd_cleanup (MM_GENERIC_GSM (object));
if (priv->pin_check_timeout) {
g_source_remove (priv->pin_check_timeout);
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index c6cb7fb..4238628 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -153,6 +153,8 @@ MMModem *mm_generic_gsm_new (const char *device,
void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
+void mm_generic_gsm_ussd_cleanup (MMGenericGsm *modem);
+
gint mm_generic_gsm_get_cid (MMGenericGsm *modem);
void mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
diff --git a/src/mm-log.c b/src/mm-log.c
index 779afe7..f99f51f 100644
--- a/src/mm-log.c
+++ b/src/mm-log.c
@@ -163,6 +163,24 @@ log_handler (const gchar *log_domain,
}
gboolean
+mm_log_set_level (const char *level, GError **error)
+{
+ gboolean found = FALSE;
+ const LogDesc *diter;
+
+ for (diter = &level_descs[0]; diter->name; diter++) {
+ if (!strcasecmp (diter->name, level)) {
+ log_level = diter->num;
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ g_set_error (error, 0, 0, "Unknown log level '%s'", level);
+ return found;
+}
+
+gboolean
mm_log_setup (const char *level,
const char *log_file,
gboolean show_timestamps,
@@ -170,23 +188,8 @@ mm_log_setup (const char *level,
GError **error)
{
/* levels */
- if (level && strlen (level)) {
- gboolean found = FALSE;
- const LogDesc *diter;
-
- for (diter = &level_descs[0]; diter->name; diter++) {
- if (!strcasecmp (diter->name, level)) {
- log_level = diter->num;
- found = TRUE;
- break;
- }
- }
-
- if (!found) {
- g_set_error (error, 0, 0, "Unknown log level '%s'", level);
- return FALSE;
- }
- }
+ if (level && strlen (level) && !mm_log_set_level (level, error))
+ return FALSE;
if (show_timestamps)
ts_flags = TS_FLAG_WALL;
diff --git a/src/mm-log.h b/src/mm-log.h
index 9b0d875..6024c08 100644
--- a/src/mm-log.h
+++ b/src/mm-log.h
@@ -47,6 +47,8 @@ void _mm_log (const char *loc,
const char *fmt,
...) __attribute__((__format__ (__printf__, 4, 5)));
+gboolean mm_log_set_level (const char *level, GError **error);
+
gboolean mm_log_setup (const char *level,
const char *log_file,
gboolean show_ts,
diff --git a/src/mm-manager.c b/src/mm-manager.c
index b9076ee..1e9403c 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -30,6 +30,10 @@ static gboolean impl_manager_enumerate_devices (MMManager *manager,
GPtrArray **devices,
GError **err);
+static gboolean impl_manager_set_logging (MMManager *manager,
+ const char *level,
+ GError **error);
+
#include "mm-manager-glue.h"
G_DEFINE_TYPE (MMManager, mm_manager, G_TYPE_OBJECT)
@@ -912,12 +916,26 @@ handle_uevent (GUdevClient *client,
/* We only care about tty/net devices when adding modem ports,
* but for remove, also handle usb parent device remove events
*/
- if ((!strcmp (action, "add") || !strcmp (action, "move")) && strcmp (subsys, "usb") !=0 )
+ if ( (!strcmp (action, "add") || !strcmp (action, "move") || !strcmp (action, "change"))
+ && (strcmp (subsys, "usb") != 0))
device_added (self, device);
else if (!strcmp (action, "remove"))
device_removed (self, device);
}
+static gboolean
+impl_manager_set_logging (MMManager *manager,
+ const char *level,
+ GError **error)
+{
+ if (mm_log_set_level (level, error)) {
+ mm_info ("logging: level '%s'", level);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
void
mm_manager_start (MMManager *manager)
{
diff --git a/src/mm-modem-gsm-ussd.c b/src/mm-modem-gsm-ussd.c
index f90a845..614999c 100644
--- a/src/mm-modem-gsm-ussd.c
+++ b/src/mm-modem-gsm-ussd.c
@@ -137,6 +137,32 @@ mm_modem_gsm_ussd_cancel (MMModemGsmUssd *self,
}
+char*
+mm_modem_gsm_ussd_encode (MMModemGsmUssd *self,
+ const char* command,
+ guint *schema)
+{
+ if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->encode)
+ return MM_MODEM_GSM_USSD_GET_INTERFACE (self)->encode(self,
+ command,
+ schema);
+ else
+ return NULL;
+}
+
+char*
+mm_modem_gsm_ussd_decode (MMModemGsmUssd *self,
+ const char* reply,
+ guint schema)
+{
+ if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->decode)
+ return MM_MODEM_GSM_USSD_GET_INTERFACE (self)->decode(self,
+ reply,
+ schema);
+ else
+ return NULL;
+}
+
/*****************************************************************************/
typedef struct {
diff --git a/src/mm-modem-gsm-ussd.h b/src/mm-modem-gsm-ussd.h
index c8f652b..04d2be8 100644
--- a/src/mm-modem-gsm-ussd.h
+++ b/src/mm-modem-gsm-ussd.h
@@ -54,6 +54,14 @@ struct _MMModemGsmUssd {
void (*cancel) (MMModemGsmUssd *modem,
MMModemFn callback,
gpointer user_data);
+
+ gchar* (*encode) (MMModemGsmUssd *modem,
+ const char* command,
+ guint *scheme);
+
+ gchar* (*decode) (MMModemGsmUssd *modem,
+ const char* command,
+ guint scheme);
};
GType mm_modem_gsm_ussd_get_type (void);
@@ -72,4 +80,16 @@ void mm_modem_gsm_ussd_cancel (MMModemGsmUssd *self,
MMModemFn callback,
gpointer user_data);
+/* CBS data coding scheme - 3GPP TS 23.038 */
+#define MM_MODEM_GSM_USSD_SCHEME_7BIT 0b00001111;
+#define MM_MODEM_GSM_USSD_SCHEME_UCS2 0b01001000;
+
+char *mm_modem_gsm_ussd_encode (MMModemGsmUssd *self,
+ const char* command,
+ guint *scheme);
+
+char *mm_modem_gsm_ussd_decode (MMModemGsmUssd *self,
+ const char* reply,
+ guint scheme);
+
#endif /* MM_MODEM_GSM_USSD_H */
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 8d033a7..0777021 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -272,8 +272,6 @@ supports_task_dispose (GObject *object)
g_object_unref (priv->port);
g_free (priv->physdev_path);
g_free (priv->driver);
- g_free (priv->probe_resp);
- g_clear_error (&(priv->probe_error));
for (iter = priv->custom; iter; iter = g_slist_next (iter)) {
CustomInit *custom = iter->data;
@@ -299,6 +297,9 @@ supports_task_dispose (GObject *object)
g_object_unref (priv->qcdm_port);
}
+ g_free (priv->probe_resp);
+ g_clear_error (&(priv->probe_error));
+
G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object);
}
@@ -398,6 +399,7 @@ static const char *dq_strings[] = {
"os_logids.h",
/* Sierra CnS port */
"NETWORK SERVICE CHANGE",
+ "/SRC/AMSS",
NULL
};
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
index 18a616d..46050cf 100644
--- a/src/mm-serial-port.c
+++ b/src/mm-serial-port.c
@@ -45,6 +45,7 @@ enum {
PROP_SEND_DELAY,
PROP_FD,
PROP_SPEW_CONTROL,
+ PROP_FLASH_OK,
LAST_PROP
};
@@ -69,6 +70,7 @@ typedef struct {
guint stopbits;
guint64 send_delay;
gboolean spew_control;
+ gboolean flash_ok;
guint queue_id;
guint watch_id;
@@ -627,8 +629,12 @@ data_available (GIOChannel *source,
gsize bytes_read;
GIOStatus status;
MMQueueData *info;
+ const char *device;
if (condition & G_IO_HUP) {
+ device = mm_port_get_device (MM_PORT (self));
+ mm_dbg ("(%s) unexpected port hangup!", device);
+
if (priv->response->len)
g_byte_array_remove_range (priv->response, 0, priv->response->len);
mm_serial_port_close_force (self);
@@ -719,6 +725,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
char *devfile;
const char *device;
struct serial_struct sinfo;
+ GTimeVal tv_start, tv_end;
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
@@ -733,6 +740,8 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
mm_info ("(%s) opening serial port...", device);
+ g_get_current_time (&tv_start);
+
/* Only open a new file descriptor if we weren't given one already */
if (priv->fd < 0) {
devfile = g_strdup_printf ("/dev/%s", device);
@@ -781,6 +790,11 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
ioctl (priv->fd, TIOCSSERIAL, &sinfo);
}
+ g_get_current_time (&tv_end);
+
+ if (tv_end.tv_sec - tv_start.tv_sec > 7)
+ mm_warn ("(%s): open blocked by driver for more than 7 seconds!", device);
+
priv->channel = g_io_channel_unix_new (priv->fd);
g_io_channel_set_encoding (priv->channel, NULL, NULL);
priv->watch_id = g_io_add_watch (priv->channel,
@@ -870,8 +884,8 @@ mm_serial_port_close (MMSerialPort *self)
* that data to send before giving up and returning from close().
* Log that. See GNOME bug #630670 for more details.
*/
- if (tv_end.tv_sec - tv_start.tv_sec > 20)
- mm_warn ("(%s): close blocked by driver for more than 20 seconds!", device);
+ if (tv_end.tv_sec - tv_start.tv_sec > 7)
+ mm_warn ("(%s): close blocked by driver for more than 7 seconds!", device);
}
/* Clear the command queue */
@@ -995,13 +1009,6 @@ mm_serial_port_queue_command_cached (MMSerialPort *self,
internal_queue_command (self, command, take_command, TRUE, timeout_seconds, callback, user_data);
}
-typedef struct {
- MMSerialPort *port;
- speed_t current_speed;
- MMSerialFlashFn callback;
- gpointer user_data;
-} FlashInfo;
-
static gboolean
get_speed (MMSerialPort *self, speed_t *speed, GError **error)
{
@@ -1076,6 +1083,13 @@ set_speed (MMSerialPort *self, speed_t speed, GError **error)
return TRUE;
}
+typedef struct {
+ MMSerialPort *port;
+ speed_t current_speed;
+ MMSerialFlashFn callback;
+ gpointer user_data;
+} FlashInfo;
+
static gboolean
flash_do (gpointer data)
{
@@ -1085,13 +1099,15 @@ flash_do (gpointer data)
priv->flash_id = 0;
- if (info->current_speed) {
- if (!set_speed (info->port, info->current_speed, &error))
- g_assert (error);
- } else {
- error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_FLASH_FAILED,
- "Failed to retrieve current speed");
+ if (priv->flash_ok) {
+ if (info->current_speed) {
+ if (!set_speed (info->port, info->current_speed, &error))
+ g_assert (error);
+ } else {
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_FLASH_FAILED,
+ "Failed to retrieve current speed");
+ }
}
info->callback (info->port, error, info->user_data);
@@ -1107,9 +1123,8 @@ mm_serial_port_flash (MMSerialPort *self,
MMSerialFlashFn callback,
gpointer user_data)
{
- FlashInfo *info;
+ FlashInfo *info = NULL;
MMSerialPortPrivate *priv;
- speed_t cur_speed = 0;
GError *error = NULL;
gboolean success;
@@ -1122,43 +1137,45 @@ mm_serial_port_flash (MMSerialPort *self,
error = g_error_new_literal (MM_SERIAL_ERROR,
MM_SERIAL_ERROR_NOT_OPEN,
"The serial port is not open.");
- callback (self, error, user_data);
- g_error_free (error);
- return FALSE;
+ goto error;
}
if (priv->flash_id > 0) {
error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS,
"Modem is already being flashed.");
- callback (self, error, user_data);
- g_error_free (error);
- return FALSE;
- }
-
- success = get_speed (self, &cur_speed, &error);
- if (!success && !ignore_errors) {
- callback (self, error, user_data);
- g_error_free (error);
- return FALSE;
+ goto error;
}
- g_clear_error (&error);
info = g_slice_new0 (FlashInfo);
info->port = self;
- info->current_speed = cur_speed;
info->callback = callback;
info->user_data = user_data;
- success = set_speed (self, B0, &error);
- if (!success && !ignore_errors) {
- callback (self, error, user_data);
- g_error_free (error);
- return FALSE;
- }
+ if (priv->flash_ok) {
+ /* Grab current speed so we can reset it after flashing */
+ success = get_speed (self, &info->current_speed, &error);
+ if (!success && !ignore_errors)
+ goto error;
+ g_clear_error (&error);
+
+ success = set_speed (self, B0, &error);
+ if (!success && !ignore_errors)
+ goto error;
+ g_clear_error (&error);
+
+ priv->flash_id = g_timeout_add (flash_time, flash_do, info);
+ } else
+ priv->flash_id = g_idle_add (flash_do, info);
- priv->flash_id = g_timeout_add (flash_time, flash_do, info);
return TRUE;
+
+error:
+ callback (self, error, user_data);
+ g_clear_error (&error);
+ if (info)
+ g_slice_free (FlashInfo, info);
+ return FALSE;
}
void
@@ -1176,6 +1193,14 @@ mm_serial_port_flash_cancel (MMSerialPort *self)
}
}
+gboolean
+mm_serial_port_get_flash_ok (MMSerialPort *self)
+{
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), TRUE);
+
+ return MM_SERIAL_PORT_GET_PRIVATE (self)->flash_ok;
+}
+
/*****************************************************************************/
MMSerialPort *
@@ -1276,6 +1301,9 @@ set_property (GObject *object, guint prop_id,
case PROP_SPEW_CONTROL:
priv->spew_control = g_value_get_boolean (value);
break;
+ case PROP_FLASH_OK:
+ priv->flash_ok = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1310,6 +1338,9 @@ get_property (GObject *object, guint prop_id,
case PROP_SPEW_CONTROL:
g_value_set_boolean (value, priv->spew_control);
break;
+ case PROP_FLASH_OK:
+ g_value_set_boolean (value, priv->flash_ok);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1420,6 +1451,15 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
FALSE,
G_PARAM_READWRITE));
+ g_object_class_install_property
+ (object_class, PROP_FLASH_OK,
+ g_param_spec_boolean (MM_SERIAL_PORT_FLASH_OK,
+ "FlaskOk",
+ "Flashing the port (0 baud for a short period) "
+ "is allowed.",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
/* 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 57ef94b..ae38017 100644
--- a/src/mm-serial-port.h
+++ b/src/mm-serial-port.h
@@ -37,6 +37,7 @@
#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 */
+#define MM_SERIAL_PORT_FLASH_OK "flash-ok" /* Construct-only */
typedef struct _MMSerialPort MMSerialPort;
typedef struct _MMSerialPortClass MMSerialPortClass;
@@ -121,8 +122,11 @@ gboolean mm_serial_port_flash (MMSerialPort *self,
gboolean ignore_errors,
MMSerialFlashFn callback,
gpointer user_data);
+
void mm_serial_port_flash_cancel (MMSerialPort *self);
+gboolean mm_serial_port_get_flash_ok (MMSerialPort *self);
+
void mm_serial_port_queue_command (MMSerialPort *self,
GByteArray *command,
gboolean take_command,
diff --git a/src/mm-sms-utils.c b/src/mm-sms-utils.c
new file mode 100644
index 0000000..3f56a64
--- /dev/null
+++ b/src/mm-sms-utils.c
@@ -0,0 +1,389 @@
+/* -*- 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 Red Hat, Inc.
+ */
+
+#include <glib.h>
+
+#include "mm-charsets.h"
+#include "mm-errors.h"
+#include "mm-utils.h"
+#include "mm-sms-utils.h"
+
+#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_NUMBER_TYPE_MASK 0x70
+#define SMS_NUMBER_TYPE_UNKNOWN 0x00
+#define SMS_NUMBER_TYPE_INTL 0x10
+#define SMS_NUMBER_TYPE_ALPHA 0x50
+
+#define SMS_NUMBER_PLAN_MASK 0x0f
+#define SMS_NUMBER_PLAN_TELEPHONE 0x01
+
+#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)
+
+typedef enum {
+ MM_SMS_ENCODING_UNKNOWN = 0x0,
+ MM_SMS_ENCODING_GSM7,
+ MM_SMS_ENCODING_8BIT,
+ MM_SMS_ENCODING_UCS2
+} SmsEncoding;
+
+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 semi-octets */
+static char *
+sms_decode_address (const guint8 *address, int len)
+{
+ guint8 addrtype, addrplan;
+ char *utf8;
+
+ addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
+ addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
+ address++;
+
+ if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
+ guint8 *unpacked;
+ guint32 unpacked_len;
+ unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
+ utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
+ unpacked_len);
+ g_free(unpacked);
+ } else if (addrtype == SMS_NUMBER_TYPE_INTL &&
+ addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
+ /* International telphone number, format as "+1234567890" */
+ utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */
+ utf8[0] = '+';
+ sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2);
+ } else {
+ /*
+ * All non-alphanumeric types and plans are just digits, but
+ * don't apply any special formatting if we don't know the
+ * format.
+ */
+ utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */
+ 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 SmsEncoding
+sms_encoding_type (int dcs)
+{
+ SmsEncoding scheme = MM_SMS_ENCODING_UNKNOWN;
+
+ switch ((dcs >> 4) & 0xf) {
+ /* General data coding group */
+ case 0: case 1:
+ case 2: case 3:
+ switch (dcs & 0x0c) {
+ case 0x08:
+ scheme = MM_SMS_ENCODING_UCS2;
+ break;
+ case 0x00:
+ /* fallthrough */
+ /* reserved - spec says to treat it as default alphabet */
+ case 0x0c:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+ case 0x04:
+ scheme = MM_SMS_ENCODING_8BIT;
+ break;
+ }
+ break;
+
+ /* Message waiting group (default alphabet) */
+ case 0xc:
+ case 0xd:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+
+ /* Message waiting group (UCS2 alphabet) */
+ case 0xe:
+ scheme = MM_SMS_ENCODING_UCS2;
+ break;
+
+ /* Data coding/message class group */
+ case 0xf:
+ switch (dcs & 0x04) {
+ case 0x00:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+ case 0x04:
+ scheme = MM_SMS_ENCODING_8BIT;
+ break;
+ }
+ break;
+
+ /* Reserved coding group values - spec says to treat it as default alphabet */
+ default:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+ }
+
+ return scheme;
+
+}
+
+static char *
+sms_decode_text (const guint8 *text, int len, SmsEncoding encoding, int bit_offset)
+{
+ char *utf8;
+ guint8 *unpacked;
+ guint32 unpacked_len;
+
+ if (encoding == MM_SMS_ENCODING_GSM7) {
+ 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 (encoding == MM_SMS_ENCODING_UCS2)
+ utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL);
+ else if (encoding == MM_SMS_ENCODING_8BIT)
+ utf8 = g_strndup ((const char *)text, len);
+ else
+ utf8 = g_strdup ("");
+
+ return utf8;
+}
+
+static void
+simple_free_gvalue (gpointer data)
+{
+ g_value_unset ((GValue *) data);
+ g_slice_free (GValue, data);
+}
+
+
+
+static GValue *
+simple_uint_value (guint32 i)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, 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)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+
+ return val;
+}
+
+GHashTable *
+sms_parse_pdu (const char *hexpdu, GError **error)
+{
+ 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, bit_offset;
+ char *smsc_addr, *sender_addr, *sc_timestamp, *msg_text;
+ SmsEncoding user_data_encoding;
+
+ /* Convert PDU from hex to binary */
+ pdu = (guint8 *) utils_hexstr2bin (hexpdu, &pdu_len);
+ if (!pdu) {
+ *error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "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) {
+ *error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "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) {
+ *error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "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_encoding = sms_encoding_type(pdu[tp_dcs_offset]);
+ if (user_data_encoding == MM_SMS_ENCODING_GSM7)
+ 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) {
+ *error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "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) {
+ *error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Unhandled message type: 0x%02x",
+ pdu[msg_start_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_encoding == MM_SMS_ENCODING_GSM7) {
+ /*
+ * Find the number of bits we need to add to the length of the
+ * user data to get a multiple of 7 (the padding).
+ */
+ bit_offset = (7 - udhl % 7) % 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_encoding, 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 (pdu[tp_dcs_offset] & SMS_DCS_CLASS_VALID)
+ g_hash_table_insert (properties, "class",
+ simple_uint_value (pdu[tp_dcs_offset] &
+ 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;
+}
diff --git a/src/mm-sms-utils.h b/src/mm-sms-utils.h
new file mode 100644
index 0000000..26d9829
--- /dev/null
+++ b/src/mm-sms-utils.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_SMS_UTILS_H
+#define MM_SMS_UTILS_H
+
+#include <glib.h>
+
+#define SMS_MAX_PDU_LEN 344
+
+GHashTable *sms_parse_pdu (const char *hexpdu, GError **error);
+
+#endif /* MM_SMS_UTILS_H */
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index e265bc1..cc47e66 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -4,7 +4,8 @@ INCLUDES = \
noinst_PROGRAMS = \
test-modem-helpers \
test-charsets \
- test-qcdm-serial-port
+ test-qcdm-serial-port \
+ test-sms
test_modem_helpers_SOURCES = \
test-modem-helpers.c
@@ -40,12 +41,23 @@ test_qcdm_serial_port_LDADD = \
$(top_builddir)/libqcdm/src/libqcdm.la \
-lutil
+test_sms_SOURCES = \
+ test-sms.c
+
+test_sms_CFLAGS = \
+ $(MM_CFLAGS)
+
+test_sms_LDADD = \
+ $(top_builddir)/src/libmodem-helpers.la \
+ $(MM_LIBS)
+
if WITH_TESTS
check-local: test-modem-helpers
$(abs_builddir)/test-modem-helpers
$(abs_builddir)/test-charsets
$(abs_builddir)/test-qcdm-serial-port
+ $(abs_builddir)/test-sms
endif
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 9be9f06..94793ef 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -35,7 +35,7 @@ POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
noinst_PROGRAMS = test-modem-helpers$(EXEEXT) test-charsets$(EXEEXT) \
- test-qcdm-serial-port$(EXEEXT)
+ test-qcdm-serial-port$(EXEEXT) test-sms$(EXEEXT)
subdir = src/tests
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -71,6 +71,13 @@ test_qcdm_serial_port_DEPENDENCIES = $(am__DEPENDENCIES_1) \
$(top_builddir)/src/libserial.la \
$(top_builddir)/src/libmodem-helpers.la \
$(top_builddir)/libqcdm/src/libqcdm.la
+am_test_sms_OBJECTS = test_sms-test-sms.$(OBJEXT)
+test_sms_OBJECTS = $(am_test_sms_OBJECTS)
+test_sms_DEPENDENCIES = $(top_builddir)/src/libmodem-helpers.la \
+ $(am__DEPENDENCIES_1)
+test_sms_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_sms_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -98,9 +105,9 @@ AM_V_GEN = $(am__v_GEN_$(V))
am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
am__v_GEN_0 = @echo " GEN " $@;
SOURCES = $(test_charsets_SOURCES) $(test_modem_helpers_SOURCES) \
- $(test_qcdm_serial_port_SOURCES)
+ $(test_qcdm_serial_port_SOURCES) $(test_sms_SOURCES)
DIST_SOURCES = $(test_charsets_SOURCES) $(test_modem_helpers_SOURCES) \
- $(test_qcdm_serial_port_SOURCES)
+ $(test_qcdm_serial_port_SOURCES) $(test_sms_SOURCES)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -292,6 +299,16 @@ test_qcdm_serial_port_LDADD = \
$(top_builddir)/libqcdm/src/libqcdm.la \
-lutil
+test_sms_SOURCES = \
+ test-sms.c
+
+test_sms_CFLAGS = \
+ $(MM_CFLAGS)
+
+test_sms_LDADD = \
+ $(top_builddir)/src/libmodem-helpers.la \
+ $(MM_LIBS)
+
all: all-am
.SUFFIXES:
@@ -344,6 +361,9 @@ test-modem-helpers$(EXEEXT): $(test_modem_helpers_OBJECTS) $(test_modem_helpers_
test-qcdm-serial-port$(EXEEXT): $(test_qcdm_serial_port_OBJECTS) $(test_qcdm_serial_port_DEPENDENCIES)
@rm -f test-qcdm-serial-port$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(test_qcdm_serial_port_OBJECTS) $(test_qcdm_serial_port_LDADD) $(LIBS)
+test-sms$(EXEEXT): $(test_sms_OBJECTS) $(test_sms_DEPENDENCIES)
+ @rm -f test-sms$(EXEEXT)
+ $(AM_V_CCLD)$(test_sms_LINK) $(test_sms_OBJECTS) $(test_sms_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -354,6 +374,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_charsets-test-charsets.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_modem_helpers-test-modem-helpers.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_qcdm_serial_port-test-qcdm-serial-port.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sms-test-sms.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -430,6 +451,22 @@ test_qcdm_serial_port-test-qcdm-serial-port.obj: test-qcdm-serial-port.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_qcdm_serial_port_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_qcdm_serial_port-test-qcdm-serial-port.obj `if test -f 'test-qcdm-serial-port.c'; then $(CYGPATH_W) 'test-qcdm-serial-port.c'; else $(CYGPATH_W) '$(srcdir)/test-qcdm-serial-port.c'; fi`
+test_sms-test-sms.o: test-sms.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_sms_CFLAGS) $(CFLAGS) -MT test_sms-test-sms.o -MD -MP -MF $(DEPDIR)/test_sms-test-sms.Tpo -c -o test_sms-test-sms.o `test -f 'test-sms.c' || echo '$(srcdir)/'`test-sms.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms-test-sms.Tpo $(DEPDIR)/test_sms-test-sms.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-sms.c' object='test_sms-test-sms.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_sms_CFLAGS) $(CFLAGS) -c -o test_sms-test-sms.o `test -f 'test-sms.c' || echo '$(srcdir)/'`test-sms.c
+
+test_sms-test-sms.obj: test-sms.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_sms_CFLAGS) $(CFLAGS) -MT test_sms-test-sms.obj -MD -MP -MF $(DEPDIR)/test_sms-test-sms.Tpo -c -o test_sms-test-sms.obj `if test -f 'test-sms.c'; then $(CYGPATH_W) 'test-sms.c'; else $(CYGPATH_W) '$(srcdir)/test-sms.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms-test-sms.Tpo $(DEPDIR)/test_sms-test-sms.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-sms.c' object='test_sms-test-sms.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_sms_CFLAGS) $(CFLAGS) -c -o test_sms-test-sms.obj `if test -f 'test-sms.c'; then $(CYGPATH_W) 'test-sms.c'; else $(CYGPATH_W) '$(srcdir)/test-sms.c'; fi`
+
mostlyclean-libtool:
-rm -f *.lo
@@ -641,6 +678,7 @@ uninstall-am:
@WITH_TESTS_TRUE@ $(abs_builddir)/test-modem-helpers
@WITH_TESTS_TRUE@ $(abs_builddir)/test-charsets
@WITH_TESTS_TRUE@ $(abs_builddir)/test-qcdm-serial-port
+@WITH_TESTS_TRUE@ $(abs_builddir)/test-sms
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/tests/test-sms.c b/src/tests/test-sms.c
new file mode 100644
index 0000000..bd18c0b
--- /dev/null
+++ b/src/tests/test-sms.c
@@ -0,0 +1,429 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2011 The Chromium OS Authors.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <string.h>
+
+#include "mm-sms-utils.h"
+#include "mm-utils.h"
+
+
+#define TEST_ENTRY_EQ(hash, key, expectvalue) do { \
+ GValue *value; \
+ value = g_hash_table_lookup((hash), (key)); \
+ g_assert(value); \
+ g_assert(G_VALUE_HOLDS_STRING(value)); \
+ g_assert_cmpstr(g_value_get_string(value), ==, (expectvalue)); \
+ } while (0)
+
+static void
+test_pdu1 (void *f, gpointer d)
+{
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x04, 0x44, 0x29, 0x61, 0xf4,
+ 0x04, 0x0b, 0x91, 0x61, 0x71, 0x95, 0x72, 0x91,
+ 0xf8, 0x00, 0x00, 0x11, 0x20, 0x82, 0x11, 0x05,
+ 0x05, 0x0a,
+ // user data:
+ 0x6a, 0xc8, 0xb2, 0xbc, 0x7c, 0x9a, 0x83, 0xc2,
+ 0x20, 0xf6, 0xdb, 0x7d, 0x2e, 0xcb, 0x41, 0xed,
+ 0xf2, 0x7c, 0x1e, 0x3e, 0x97, 0x41, 0x1b, 0xde,
+ 0x06, 0x75, 0x4f, 0xd3, 0xd1, 0xa0, 0xf9, 0xbb,
+ 0x5d, 0x06, 0x95, 0xf1, 0xf4, 0xb2, 0x9b, 0x5c,
+ 0x26, 0x83, 0xc6, 0xe8, 0xb0, 0x3c, 0x3c, 0xa6,
+ 0x97, 0xe5, 0xf3, 0x4d, 0x6a, 0xe3, 0x03, 0xd1,
+ 0xd1, 0xf2, 0xf7, 0xdd, 0x0d, 0x4a, 0xbb, 0x59,
+ 0xa0, 0x79, 0x7d, 0x8c, 0x06, 0x85, 0xe7, 0xa0,
+ 0x00, 0x28, 0xec, 0x26, 0x83, 0x2a, 0x96, 0x0b,
+ 0x28, 0xec, 0x26, 0x83, 0xbe, 0x60, 0x50, 0x78,
+ 0x0e, 0xba, 0x97, 0xd9, 0x6c, 0x17};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12404492164");
+ TEST_ENTRY_EQ (sms, "number", "+16175927198");
+ TEST_ENTRY_EQ (sms, "timestamp", "110228115050-05");
+ TEST_ENTRY_EQ (sms, "text",
+ "Here's a longer message [{with some extended characters}] "
+ "thrown in, such as £ and ΩΠΨ and §¿ as well.");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+static void
+test_pdu2 (void *f, gpointer d)
+{
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x97, 0x30, 0x07, 0x11, 0x11, 0xf1,
+ 0x04, 0x14, 0xd0, 0x49, 0x37, 0xbd, 0x2c, 0x77,
+ 0x97, 0xe9, 0xd3, 0xe6, 0x14, 0x00, 0x08, 0x11,
+ 0x30, 0x92, 0x91, 0x02, 0x40, 0x61, 0x08, 0x04,
+ 0x42, 0x04, 0x35, 0x04, 0x41, 0x04, 0x42};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+79037011111");
+ TEST_ENTRY_EQ (sms, "number", "InternetSMS");
+ TEST_ENTRY_EQ (sms, "timestamp", "110329192004+04");
+ TEST_ENTRY_EQ (sms, "text", "тест");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+static void
+test_pdu3 (void *f, gpointer d)
+{
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12345678901");
+ TEST_ENTRY_EQ (sms, "number", "+18005551212");
+ TEST_ENTRY_EQ (sms, "timestamp", "110101123456+00");
+ TEST_ENTRY_EQ (sms, "text", "hellohello");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+
+static void
+test_pdu3_nzpid (void *f, gpointer d)
+{
+ /* pid is nonzero (00 -> ff) */
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0xff, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12345678901");
+ TEST_ENTRY_EQ (sms, "number", "+18005551212");
+ TEST_ENTRY_EQ (sms, "timestamp", "110101123456+00");
+ TEST_ENTRY_EQ (sms, "text", "hellohello");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+
+
+static void
+test_pdu3_mms (void *f, gpointer d)
+{
+ /* mms is clear (04 -> 00) */
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x00, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12345678901");
+ TEST_ENTRY_EQ (sms, "number", "+18005551212");
+ TEST_ENTRY_EQ (sms, "timestamp", "110101123456+00");
+ TEST_ENTRY_EQ (sms, "text", "hellohello");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+
+static void
+test_pdu3_natl (void *f, gpointer d)
+{
+ /* number is natl (91 -> 81) */
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x04, 0x0b, 0x81, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12345678901");
+ TEST_ENTRY_EQ (sms, "number", "18005551212"); /* no plus */
+ TEST_ENTRY_EQ (sms, "timestamp", "110101123456+00");
+ TEST_ENTRY_EQ (sms, "text", "hellohello");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+
+static void
+test_pdu3_8bit (void *f, gpointer d)
+{
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0x00, 0x04, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37, 0xde};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12345678901");
+ TEST_ENTRY_EQ (sms, "number", "+18005551212");
+ TEST_ENTRY_EQ (sms, "timestamp", "110101123456+00");
+ TEST_ENTRY_EQ (sms, "text", "\xe8\x32\x9b\xfd\x46\x97\xd9\xec\x37\xde");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+
+static void
+test_pdu_dcsf1 (void *f, gpointer d)
+{
+ /* TP-DCS coding scheme is group F */
+ static const guint8 pdu[] = {
+ 0x07, // length of SMSC info
+ 0x91, // type of address of SMSC (E.164)
+ 0x33, 0x06, 0x09, 0x10, 0x93, 0xF0, // SMSC address (+33 60 90 01 39 0)
+ 0x04, // SMS-DELIVER
+ 0x04, // address length
+ 0x85, // type of address
+ 0x81, 0x00, // sender address (1800)
+ 0x00, // TP-PID protocol identifier
+ 0xF1, // TP-DCS data coding scheme
+ 0x11, 0x60, 0x42, 0x31, 0x80, 0x51, 0x80, // timestamp 11-06-24 13:08:51
+ 0xA0, // TP-UDL user data length (160)
+ // Content:
+ 0x49,
+ 0xB7, 0xF9, 0x0D, 0x9A, 0x1A, 0xA5, 0xA0, 0x16,
+ 0x68, 0xF8, 0x76, 0x9B, 0xD3, 0xE4, 0xB2, 0x9B,
+ 0x9E, 0x2E, 0xB3, 0x59, 0xA0, 0x3F, 0xC8, 0x5D,
+ 0x06, 0xA9, 0xC3, 0xED, 0x70, 0x7A, 0x0E, 0xA2,
+ 0xCB, 0xC3, 0xEE, 0x79, 0xBB, 0x4C, 0xA7, 0xCB,
+ 0xCB, 0xA0, 0x56, 0x43, 0x61, 0x7D, 0xA7, 0xC7,
+ 0x69, 0x90, 0xFD, 0x4D, 0x97, 0x97, 0x41, 0xEE,
+ 0x77, 0xDD, 0x5E, 0x0E, 0xD7, 0x41, 0xED, 0x37,
+ 0x1D, 0x44, 0x2E, 0x83, 0xE0, 0xE1, 0xF9, 0xBC,
+ 0x0C, 0xD2, 0x81, 0xE6, 0x77, 0xD9, 0xB8, 0x4C,
+ 0x06, 0xC1, 0xDF, 0x75, 0x39, 0xE8, 0x5C, 0x90,
+ 0x97, 0xE5, 0x20, 0xFB, 0x9B, 0x2E, 0x2F, 0x83,
+ 0xC6, 0xEF, 0x36, 0x9C, 0x5E, 0x06, 0x4D, 0x8D,
+ 0x52, 0xD0, 0xBC, 0x2E, 0x07, 0xDD, 0xEF, 0x77,
+ 0xD7, 0xDC, 0x2C, 0x77, 0x99, 0xE5, 0xA0, 0x77,
+ 0x1D, 0x04, 0x0F, 0xCB, 0x41, 0xF4, 0x02, 0xBB,
+ 0x00, 0x47, 0xBF, 0xDD, 0x65, 0x50, 0xB8, 0x0E,
+ 0xCA, 0xD9, 0x66};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+33609001390");
+ TEST_ENTRY_EQ (sms, "number", "1800");
+ TEST_ENTRY_EQ (sms, "timestamp", "110624130815+02");
+ TEST_ENTRY_EQ (sms, "text",
+ "Info SFR - Confidentiel, à ne jamais transmettre -\r\n"
+ "Voici votre nouveau mot de passe : sw2ced pour gérer "
+ "votre compte SFR sur www.sfr.fr ou par téléphone au 963");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+
+static void
+test_pdu_dcsf_8bit (void *f, gpointer d)
+{
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0x00, 0xf4, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0a, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37, 0xde};
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+12345678901");
+ TEST_ENTRY_EQ (sms, "number", "+18005551212");
+ TEST_ENTRY_EQ (sms, "timestamp", "110101123456+00");
+ TEST_ENTRY_EQ (sms, "text", "\xe8\x32\x9b\xfd\x46\x97\xd9\xec\x37\xde");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+
+static void
+test_pdu_insufficient_data (void *f, gpointer d)
+{
+ static const guint8 pdu[] = {
+ 0x07, 0x91, 0x21, 0x43, 0x65, 0x87, 0x09, 0xf1,
+ 0x04, 0x0b, 0x91, 0x81, 0x00, 0x55, 0x15, 0x12,
+ 0xf2, 0x00, 0x00, 0x11, 0x10, 0x10, 0x21, 0x43,
+ 0x65, 0x00, 0x0b, 0xe8, 0x32, 0x9b, 0xfd, 0x46,
+ 0x97, 0xd9, 0xec, 0x37
+ };
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu, sizeof(pdu));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms == NULL);
+
+ g_free (hexpdu);
+}
+
+
+static void
+test_pdu_udhi (void *f, gpointer d)
+{
+ /* Welcome message from KPN NL */
+ static const char *hexpdu =
+"07911356131313F64004850120390011609232239180A006080400100201D7327BFD6EB340E232"
+"1BF46E83EA7790F59D1E97DBE1341B442F83C465763D3DA797E56537C81D0ECB41AB59CC1693C1"
+"6031D96C064241E5656838AF03A96230982A269BCD462917C8FA4E8FCBED709A0D7ABBE9F6B0FB"
+"5C7683D27350984D4FABC9A0B33C4C4FCF5D20EBFB2D079DCB62793DBD06D9C36E50FB2D4E97D9"
+"A0B49B5E96BBCB";
+ GHashTable *sms;
+ GError *error;
+
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "+31653131316");
+ TEST_ENTRY_EQ (sms, "number", "1002");
+ TEST_ENTRY_EQ (sms, "timestamp", "110629233219+02");
+ TEST_ENTRY_EQ (sms, "text",
+ "Welkom, bel om uw Voicemail te beluisteren naar +31612001233"
+ " (PrePay: *100*1233#). Voicemail ontvangen is altijd gratis."
+ " Voor gebruik van mobiel interne");
+
+ g_hash_table_unref (sms);
+}
+
+#if 0
+static void
+test_pduX (void *f, gpointer d)
+{
+ GHashTable *sms;
+ GError *error;
+ char *hexpdu;
+
+ hexpdu = utils_bin2hexstr (pdu1, sizeof(pdu1));
+ sms = sms_parse_pdu (hexpdu, &error);
+ g_assert (sms);
+
+ TEST_ENTRY_EQ (sms, "smsc", "");
+ TEST_ENTRY_EQ (sms, "number", "");
+ TEST_ENTRY_EQ (sms, "timestamp", "");
+ TEST_ENTRY_EQ (sms, "text",
+ "");
+
+ g_free (hexpdu);
+ g_hash_table_unref (sms);
+}
+#endif
+
+
+
+#if GLIB_CHECK_VERSION(2,25,12)
+typedef GTestFixtureFunc TCFunc;
+#else
+typedef void (*TCFunc)(void);
+#endif
+
+#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
+
+int main (int argc, char **argv)
+{
+ GTestSuite *suite;
+ gint result;
+
+ g_type_init ();
+
+ g_test_init (&argc, &argv, NULL);
+
+ suite = g_test_get_root ();
+
+ g_test_suite_add (suite, TESTCASE (test_pdu1, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu2, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu3, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu3_nzpid, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu3_mms, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu3_natl, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu3_8bit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu_dcsf1, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu_dcsf_8bit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu_insufficient_data, NULL));
+ g_test_suite_add (suite, TESTCASE (test_pdu_udhi, NULL));
+
+ result = g_test_run ();
+
+ return result;
+}