aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:23 +0100
committerGuido Günther <agx@sigxcpu.org>2014-02-05 08:38:23 +0100
commitdc645b92b9a7db3076ae34986ac219d01677d124 (patch)
tree963a5d6ad150a88a2a8ab6d994d79d539e19383a
parent87bd9deec22af69bb27226254803ac5c63b18d78 (diff)
Imported Upstream version 0.4+git.20100624t180933.6e79d15upstream/0.4+git.20100624t180933.6e79d15
-rw-r--r--.gitignore18
-rw-r--r--Makefile.am37
-rwxr-xr-xautogen.sh1
-rw-r--r--configure.ac95
-rw-r--r--introspection/Makefile.am4
-rw-r--r--introspection/all.xml2
-rw-r--r--introspection/mm-modem-gsm-card.xml24
-rw-r--r--introspection/mm-modem-gsm-network.xml172
-rw-r--r--introspection/mm-modem-gsm-ussd.xml78
-rw-r--r--introspection/mm-modem-gsm.xml88
-rw-r--r--introspection/mm-modem-location.xml253
-rw-r--r--introspection/mm-modem.xml37
-rw-r--r--libqcdm/Makefile.am2
-rw-r--r--libqcdm/src/Makefile.am42
-rw-r--r--libqcdm/src/com.c61
-rw-r--r--libqcdm/src/com.h25
-rw-r--r--libqcdm/src/commands.c1064
-rw-r--r--libqcdm/src/commands.h437
-rw-r--r--libqcdm/src/dm-commands.h370
-rw-r--r--libqcdm/src/error.c87
-rw-r--r--libqcdm/src/error.h53
-rw-r--r--libqcdm/src/libqcdm.ver6
-rw-r--r--libqcdm/src/nv-items.h64
-rw-r--r--libqcdm/src/result-private.h51
-rw-r--r--libqcdm/src/result.c248
-rw-r--r--libqcdm/src/result.h42
-rw-r--r--libqcdm/src/utils.c311
-rw-r--r--libqcdm/src/utils.h54
-rw-r--r--libqcdm/tests/Makefile.am32
-rw-r--r--libqcdm/tests/test-qcdm-com.c1184
-rw-r--r--libqcdm/tests/test-qcdm-com.h51
-rw-r--r--libqcdm/tests/test-qcdm-crc.c65
-rw-r--r--libqcdm/tests/test-qcdm-crc.h25
-rw-r--r--libqcdm/tests/test-qcdm-escaping.c124
-rw-r--r--libqcdm/tests/test-qcdm-escaping.h26
-rw-r--r--libqcdm/tests/test-qcdm-result.c71
-rw-r--r--libqcdm/tests/test-qcdm-result.h26
-rw-r--r--libqcdm/tests/test-qcdm-utils.c86
-rw-r--r--libqcdm/tests/test-qcdm-utils.h26
-rw-r--r--libqcdm/tests/test-qcdm.c115
-rw-r--r--m4/compiler_warnings.m431
-rw-r--r--marshallers/mm-marshal.list1
-rw-r--r--org.freedesktop.ModemManager.conf.nopolkit (renamed from org.freedesktop.ModemManager.conf)3
-rw-r--r--org.freedesktop.ModemManager.conf.polkit154
-rw-r--r--plugins/77-mm-ericsson-mbm.rules10
-rw-r--r--plugins/77-mm-longcheer-port-types.rules134
-rw-r--r--plugins/77-mm-simtech-port-types.rules29
-rw-r--r--plugins/Makefile.am35
-rw-r--r--plugins/mm-modem-anydata-cdma.c86
-rw-r--r--plugins/mm-modem-gobi-gsm.c14
-rw-r--r--plugins/mm-modem-hso.c602
-rw-r--r--plugins/mm-modem-huawei-cdma.c73
-rw-r--r--plugins/mm-modem-huawei-gsm.c705
-rw-r--r--plugins/mm-modem-longcheer-gsm.c222
-rw-r--r--plugins/mm-modem-longcheer-gsm.h43
-rw-r--r--plugins/mm-modem-mbm.c578
-rw-r--r--plugins/mm-modem-mbm.h4
-rw-r--r--plugins/mm-modem-nokia.c14
-rw-r--r--plugins/mm-modem-novatel-cdma.c183
-rw-r--r--plugins/mm-modem-novatel-cdma.h45
-rw-r--r--plugins/mm-modem-novatel-gsm.c276
-rw-r--r--plugins/mm-modem-option-utils.c451
-rw-r--r--plugins/mm-modem-option.c256
-rw-r--r--plugins/mm-modem-sierra-cdma.c148
-rw-r--r--plugins/mm-modem-sierra-gsm.c268
-rw-r--r--plugins/mm-modem-simtech-gsm.c471
-rw-r--r--plugins/mm-modem-simtech-gsm.h43
-rw-r--r--plugins/mm-modem-zte.c329
-rw-r--r--plugins/mm-plugin-anydata.c30
-rw-r--r--plugins/mm-plugin-generic.c28
-rw-r--r--plugins/mm-plugin-gobi.c21
-rw-r--r--plugins/mm-plugin-hso.c11
-rw-r--r--plugins/mm-plugin-huawei.c75
-rw-r--r--plugins/mm-plugin-longcheer.c38
-rw-r--r--plugins/mm-plugin-mbm.c38
-rw-r--r--plugins/mm-plugin-mbm.h4
-rw-r--r--plugins/mm-plugin-moto-c.c31
-rw-r--r--plugins/mm-plugin-nokia.c21
-rw-r--r--plugins/mm-plugin-novatel.c40
-rw-r--r--plugins/mm-plugin-option.c24
-rw-r--r--plugins/mm-plugin-sierra.c22
-rw-r--r--plugins/mm-plugin-simtech.c189
-rw-r--r--plugins/mm-plugin-simtech.h41
-rw-r--r--plugins/mm-plugin-zte.c28
-rw-r--r--po/LINGUAS0
-rw-r--r--po/POTFILES.in5
-rw-r--r--policy/Makefile.am15
-rw-r--r--policy/modem-manager.pngbin0 -> 817 bytes
-rw-r--r--policy/org.freedesktop.modem-manager.policy.in57
-rw-r--r--src/77-mm-pcmcia-device-blacklist.rules10
-rw-r--r--src/77-mm-platform-serial-whitelist.rules14
-rw-r--r--src/77-mm-usb-device-blacklist.rules66
-rw-r--r--src/Makefile.am65
-rw-r--r--src/main.c27
-rw-r--r--src/mm-at-serial-port.c364
-rw-r--r--src/mm-at-serial-port.h85
-rw-r--r--src/mm-auth-provider-factory.c45
-rw-r--r--src/mm-auth-provider-polkit.c153
-rw-r--r--src/mm-auth-provider-polkit.h43
-rw-r--r--src/mm-auth-provider.c300
-rw-r--r--src/mm-auth-provider.h86
-rw-r--r--src/mm-auth-request-polkit.c175
-rw-r--r--src/mm-auth-request-polkit.h53
-rw-r--r--src/mm-auth-request.c182
-rw-r--r--src/mm-auth-request.h72
-rw-r--r--src/mm-callback-info.c23
-rw-r--r--src/mm-callback-info.h6
-rw-r--r--src/mm-charsets.c175
-rw-r--r--src/mm-charsets.h52
-rw-r--r--src/mm-errors.c185
-rw-r--r--src/mm-errors.h18
-rw-r--r--src/mm-generic-cdma.c975
-rw-r--r--src/mm-generic-cdma.h35
-rw-r--r--src/mm-generic-gsm.c2833
-rw-r--r--src/mm-generic-gsm.h98
-rw-r--r--src/mm-manager.c499
-rw-r--r--src/mm-manager.h4
-rw-r--r--src/mm-modem-base.c431
-rw-r--r--src/mm-modem-base.h33
-rw-r--r--src/mm-modem-cdma.c35
-rw-r--r--src/mm-modem-gsm-card.c349
-rw-r--r--src/mm-modem-gsm-card.h25
-rw-r--r--src/mm-modem-gsm-network.c175
-rw-r--r--src/mm-modem-gsm-network.h45
-rw-r--r--src/mm-modem-gsm-sms.c384
-rw-r--r--src/mm-modem-gsm.h48
-rw-r--r--src/mm-modem-helpers.c602
-rw-r--r--src/mm-modem-helpers.h32
-rw-r--r--src/mm-modem-location.c330
-rw-r--r--src/mm-modem-location.h73
-rw-r--r--src/mm-modem.c201
-rw-r--r--src/mm-modem.h71
-rw-r--r--src/mm-plugin-base.c419
-rw-r--r--src/mm-plugin-base.h13
-rw-r--r--src/mm-plugin.c14
-rw-r--r--src/mm-plugin.h6
-rw-r--r--src/mm-port.c31
-rw-r--r--src/mm-port.h5
-rw-r--r--src/mm-qcdm-serial-port.c225
-rw-r--r--src/mm-qcdm-serial-port.h66
-rw-r--r--src/mm-serial-parsers.c102
-rw-r--r--src/mm-serial-port.c610
-rw-r--r--src/mm-serial-port.h92
-rw-r--r--src/mm-utils.c78
-rw-r--r--src/mm-utils.h24
-rw-r--r--src/tests/test-modem-helpers.c453
-rw-r--r--test/lsudev.c17
147 files changed, 19988 insertions, 3127 deletions
diff --git a/.gitignore b/.gitignore
index c6c7fda..dd0a8bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ configure
depcomp
install-sh
ltmain.sh
+intltool-*
missing
stamp-h1
*~
@@ -20,10 +21,27 @@ libtool
.deps
.libs
*-glue.h
+*.tar.bz2
org.freedesktop.ModemManager.service
+org.freedesktop.ModemManager.conf
ModemManager.pc
marshallers/mm-marshal.[ch]
src/modem-manager
docs/spec.html
callouts/mm-modem-probe
+test/lsudev
+src/tests/test-modem-helpers
+policy/org.freedesktop.modem-manager.policy
+libqcdm/tests/test-qcdm
+
+po/Makefile.in.in
+po/POTFILES
+po/stamp-it
+po/.intltool-merge-cache
+po/ModemManager.pot
+
+m4/gtk-doc.m4
+m4/intltool.m4
+m4/libtool.m4
+m4/lt*.m4
diff --git a/Makefile.am b/Makefile.am
index eee0847..276a276 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,11 +19,25 @@ all: $(GENERATED_FILES)
CLEANFILES = $(GENERATED_FILES)
endif
-SUBDIRS = marshallers src plugins introspection test
+SUBDIRS = marshallers libqcdm src plugins introspection po policy test
+
+if WITH_POLKIT
+SUBDIRS += policy
+endif
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.ModemManager.conf
+dbusservice_file_polkit = org.freedesktop.ModemManager.conf.polkit
+dbusservice_file_nopolkit = org.freedesktop.ModemManager.conf.nopolkit
+
+org.freedesktop.ModemManager.conf:
+if WITH_POLKIT
+ cp -f $(top_srcdir)/$(dbusservice_file_polkit) $(dbusservice_DATA)
+else
+ cp -f $(top_srcdir)/$(dbusservice_file_nopolkit) $(dbusservice_DATA)
+endif
+
dbusactivationdir = $(datadir)/dbus-1/system-services
dbusactivation_in_files = org.freedesktop.ModemManager.service.in
dbusactivation_DATA = $(dbusactivation_in_files:.service.in=.service)
@@ -39,11 +53,24 @@ edit = @sed \
DISTCHECK_CONFIGURE_FLAGS = --with-udev-base-dir=$dc_install_base
-DISTCLEANFILES = \
- $(dbusactivation_DATA)
+INTLTOOL_FILES = \
+ intltool-extract.in \
+ intltool-merge.in \
+ intltool-update.in
+DISTCLEANFILES = \
+ $(dbusactivation_DATA) \
+ $(dbusservice_DATA) \
+ intltool-extract \
+ intltool-merge \
+ intltool-update \
+ po/.intltool-merge-cache
EXTRA_DIST = \
doc-generator.xsl \
- $(dbusservice_DATA) \
- $(dbusactivation_in_files)
+ $(dbusactivation_in_files) \
+ $(INTLTOOL_FILES) \
+ $(dbusservice_file_polkit) \
+ $(dbusservice_file_nopolkit)
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/autogen.sh b/autogen.sh
index d2ed9b5..62614d9 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -15,6 +15,7 @@ PKG_NAME=ModemManager
(cd $srcdir;
autoreconf --install --symlink &&
+ intltoolize --force &&
autoreconf &&
./configure --enable-maintainer-mode $@
)
diff --git a/configure.ac b/configure.ac
index aae718e..b763d7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,9 +1,11 @@
AC_PREREQ(2.52)
-AC_INIT(ModemManager, 0.3, dcbw@redhat.com, ModemManager)
+AC_INIT(ModemManager, 0.4, dcbw@redhat.com, ModemManager)
AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2])
AM_MAINTAINER_MODE
+AC_CONFIG_MACRO_DIR([m4])
+
AC_CONFIG_HEADERS(config.h)
dnl Required programs
@@ -12,6 +14,18 @@ AM_PROG_CC_C_O
AC_PROG_INSTALL
AC_PROG_LIBTOOL
+dnl Define _GNU_SOURCE for various things like strcasestr()
+AC_GNU_SOURCE
+
+dnl
+dnl translation support
+dnl
+GETTEXT_PACKAGE=ModemManager
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
+IT_PROG_INTLTOOL([0.35.0])
+AM_GLIB_GNU_GETTEXT
+
PKG_CHECK_MODULES(MM, dbus-glib-1 >= 0.75 glib-2.0 >= 2.18 gmodule-2.0 gobject-2.0)
PKG_CHECK_MODULES(GUDEV, gudev-1.0)
@@ -39,6 +53,22 @@ AC_SUBST(UDEV_BASE_DIR)
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
AC_SUBST(GLIB_GENMARSHAL)
+# PolicyKit
+AC_ARG_WITH(polkit, AS_HELP_STRING([--with-polkit], [Build with PolicyKit support]))
+AM_CONDITIONAL(WITH_POLKIT, test "x$with_polkit" = "xyes")
+case $with_polkit in
+ yes)
+ with_polkit=yes
+ PKG_CHECK_MODULES(POLKIT, polkit-gobject-1 >= 0.95)
+ AC_DEFINE(WITH_POLKIT, 1, [Define if you want to use PolicyKit])
+ AC_SUBST(POLKIT_CFLAGS)
+ AC_SUBST(POLKIT_LIBS)
+ ;;
+ *)
+ with_polkit=no
+ ;;
+esac
+
# PPPD
AC_CHECK_HEADERS(pppd/pppd.h, have_pppd_headers="yes", have_pppd_headers="no")
AM_CONDITIONAL(HAVE_PPPD_H, test "x$have_pppd_headers" = "xyes")
@@ -54,39 +84,10 @@ AC_ARG_WITH([pppd-plugin-dir], AS_HELP_STRING([--with-pppd-plugin-dir=DIR], [pat
if test -n "$with_pppd_plugin_dir" ; then
PPPD_PLUGIN_DIR="$with_pppd_plugin_dir"
else
- PPPD_PLUGIN_DIR="${libdir}/pppd/2.4.4"
+ PPPD_PLUGIN_DIR="${libdir}/pppd/2.4.5"
fi
AC_SUBST(PPPD_PLUGIN_DIR)
-AC_ARG_ENABLE(more-warnings,
-AS_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]), set_more_warnings="$enableval",set_more_warnings=yes)
-AC_MSG_CHECKING(for more warnings, including -Werror)
-if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
- AC_MSG_RESULT(yes)
- CFLAGS="-Wall -Werror -std=gnu89 $CFLAGS"
-
- for option in -Wshadow -Wmissing-declarations -Wmissing-prototypes \
- -Wdeclaration-after-statement -Wstrict-prototypes \
- -Wfloat-equal -Wno-unused-parameter -Wno-sign-compare \
- -fno-strict-aliasing; do
- SAVE_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $option"
- AC_MSG_CHECKING([whether gcc understands $option])
- AC_TRY_COMPILE([], [],
- has_option=yes,
- has_option=no)
- if test $has_option = no; then
- CFLAGS="$SAVE_CFLAGS"
- fi
- AC_MSG_RESULT($has_option)
- unset has_option
- unset SAVE_CFLAGS
- done
- unset option
-else
- AC_MSG_RESULT(no)
-fi
-
AC_ARG_WITH(docs, AC_HELP_STRING([--with-docs], [Build ModemManager documentation]))
AM_CONDITIONAL(WITH_DOCS, test "x$with_docs" = "xyes")
case $with_docs in
@@ -110,14 +111,42 @@ case $with_tests in
;;
esac
+NM_COMPILER_WARNINGS
+
+
+dnl
+dnl dbus-glib >= 0.86 is required for Location API support
+dnl
+with_location_api=no
+PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= 0.86, with_location_api="yes", with_location_api="no")
+if test x"$with_location_api" = xyes; then
+ AC_DEFINE(LOCATION_API, 1, [Define if you have dbus-glib 0.86 or higher])
+else
+ AC_MSG_WARN([dbus-glib >= 0.86 is required for Location API support])
+fi
+AM_CONDITIONAL(WITH_LOCATION_API, test "x$with_location_api" = "xyes")
+
+dnl
+dnl Distribution version string
+dnl
+AC_ARG_WITH(dist-version, AS_HELP_STRING([--with-dist-version=<mm-dist-version>], [Define the custom version (like distribution package name and revision)]), ac_distver=$withval, ac_distver="")
+if ! test x"$ac_distver" = x""; then
+ AC_DEFINE_UNQUOTED(MM_DIST_VERSION, "$ac_distver", [Define the distribution version string])
+fi
+
AC_CONFIG_FILES([
Makefile
marshallers/Makefile
+libqcdm/Makefile
+libqcdm/src/Makefile
+libqcdm/tests/Makefile
src/Makefile
src/tests/Makefile
plugins/Makefile
test/Makefile
introspection/Makefile
+po/Makefile.in
+policy/Makefile
])
AC_OUTPUT
@@ -130,4 +159,8 @@ echo Building documentation: ${with_docs}
echo
echo Building PPP-enabled tests: ${have_pppd_headers}
echo
+echo Building with PolicyKit support: ${with_polkit}
+echo
+echo Building with Location API support: ${with_location_api}
+echo
diff --git a/introspection/Makefile.am b/introspection/Makefile.am
index 404c1d9..941c924 100644
--- a/introspection/Makefile.am
+++ b/introspection/Makefile.am
@@ -13,4 +13,6 @@ EXTRA_DIST = \
mm-modem-gsm-network.xml \
mm-modem-gsm-sms.xml \
mm-modem-simple.xml \
- mm-serial-error.xml
+ mm-serial-error.xml \
+ mm-modem-location.xml \
+ mm-modem-gsm-ussd.xml
diff --git a/introspection/all.xml b/introspection/all.xml
index 426baf0..967e90d 100644
--- a/introspection/all.xml
+++ b/introspection/all.xml
@@ -25,6 +25,7 @@
<xi:include href="mm-manager.xml"/>
<xi:include href="mm-modem.xml"/>
<xi:include href="mm-modem-simple.xml"/>
+ <xi:include href="mm-modem-location.xml"/>
<xi:include href="mm-modem-cdma.xml"/>
<xi:include href="mm-modem-gsm.xml"/>
<xi:include href="mm-modem-gsm-card.xml"/>
@@ -32,6 +33,7 @@
<xi:include href="mm-modem-gsm-network.xml"/>
<xi:include href="mm-modem-gsm-sms.xml"/>
<xi:include href="mm-modem-gsm-hso.xml"/>
+ <xi:include href="mm-modem-gsm-ussd.xml"/>
<xi:include href="mm-serial-error.xml"/>
<xi:include href="mm-modem-error.xml"/>
diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml
index ad9348d..9c9fdc1 100644
--- a/introspection/mm-modem-gsm-card.xml
+++ b/introspection/mm-modem-gsm-card.xml
@@ -28,6 +28,20 @@
</arg>
</method>
+ <method name="GetOperatorId">
+ <tp:docstring>
+ Returns the ID of the network operator that issued the SIM card,
+ formatted as a 5 or 6-digit MCC/MNC code (ex "310410").
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_operator_id"/>
+ <arg name="imsi" type="s" direction="out">
+ <tp:docstring>
+ The operator ID formatted as an MCC/MNC code.
+ </tp:docstring>
+ </arg>
+ </method>
+
<method name="SendPuk">
<tp:docstring>
Send the PUK and a new PIN to unlock the SIM card.
@@ -96,11 +110,17 @@
</method>
<property name="SupportedBands" type="u" access="read" tp:type="MM_MODEM_GSM_BAND">
- <tp:docstring>Bands supported by the card. (Note for plugin writers: returned value must not contain ANY)</tp:docstring>
+ <tp:docstring>
+ Bands supported by the card. (Note for plugin writers:
+ returned value must not contain ANY)
+ </tp:docstring>
</property>
<property name="SupportedModes" type="u" access="read" tp:type="MM_MODEM_GSM_MODE">
- <tp:docstring>Network selection modes supported by the card. (Note for plugin writers: returned value must not contain ANY)</tp:docstring>
+ <tp:docstring>
+ Network selection modes supported by the card. (Note for plugin writers:
+ returned value must not contain ANY)
+ </tp:docstring>
</property>
</interface>
diff --git a/introspection/mm-modem-gsm-network.xml b/introspection/mm-modem-gsm-network.xml
index 934e8e0..7c26681 100644
--- a/introspection/mm-modem-gsm-network.xml
+++ b/introspection/mm-modem-gsm-network.xml
@@ -23,7 +23,38 @@
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_scan"/>
<arg name="results" type="aa{ss}" direction="out">
<tp:docstring>
- Found networks. It's an array of dictionaries (strings for keys and values), the list of known keys is the following: status, operator-long, operator-short, operator-num, access-tech.
+ <p>Found networks. It's an array of dictionaries (strings for both
+ keys and values) with each array element describing a mobile network
+ found in the scan. Each dict may include one or more of the following
+ keys:</p>
+ <ul>
+ <li>
+ "status": a number representing network availability status as
+ defined in 3GPP TS 27.007 section 7.3. e.g. "0" (unknown), "1"
+ (available), "2" (current), or "3" (forbidden). This key will
+ always be present.
+ </li>
+ <li>
+ "operator-long": long-format name of operator. If the name is
+ unknown, this field should not be present.
+ </li>
+ <li>
+ "operator-short": short-format name of operator. If the name is
+ unknown, this field should not be present.
+ </li>
+ <li>
+ "operator-num": mobile code of the operator. Returned in the
+ format "MCCMNC", where MCC is the three-digit ITU E.212 Mobile
+ Country Code and MNC is the two- or three-digit GSM Mobile
+ Network Code. e.g. "31026" or "310260".
+ </li>
+ <li>
+ "access-tech": a number representing the access technology used by
+ this mobile network as described in 3GPP TS 27.007 section 7.3.
+ e.g. "0" (GSM), "1" (GSM Compact), "2" (UTRAN/UMTS), "3" (EDGE),
+ etc.
+ </li>
+ </ul>
</tp:docstring>
</arg>
</method>
@@ -82,11 +113,12 @@
<method name="SetNetworkMode">
<tp:docstring>
- Set the desired mode the device may use when connecting to a mobile network.
+ Set the desired mode the device may use when connecting to a mobile
+ network (DEPRECATED; see SetAllowedMode instead).
</tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_set_network_mode"/>
- <arg name="mode" type="u" direction="in" tp:type="MM_MODEM_GSM_MODE">
+ <arg name="mode" type="u" direction="in" tp:type="MM_MODEM_GSM_NETWORK_DEPRECATED_MODE">
<tp:docstring>
The desired network mode. Only one mode may be specified, and may not be UNKNOWN.
</tp:docstring>
@@ -95,11 +127,14 @@
<method name="GetNetworkMode">
<tp:docstring>
- Returns the current network mode of the device. (Note for plugin writers: returned value *may* be ANY)
+ Returns the current network mode of the device (DEPRECATED; does not
+ allow returning both the saved mode preference *and* the current access
+ technology of the device at the same time. See the AllowedMode
+ property instead).
</tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_network_mode"/>
- <arg name="mode" type="u" direction="out" tp:type="MM_MODEM_GSM_MODE">
+ <arg name="mode" type="u" direction="out" tp:type="MM_MODEM_GSM_NETWORK_DEPRECATED_MODE">
<tp:docstring>
Returns the general network mode (ex. 2G/3G preference) of the device.
</tp:docstring>
@@ -114,14 +149,65 @@
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_get_reg_info"/>
<arg name="info" type="(uss)" direction="out">
<tp:docstring>
- The returned information contains:
- * Network status.
- * Current operator code.
- * Current operator name,
+ The returned information is composed of the following items in the
+ following order:
+ <ul>
+ <li>
+ Mobile registration status as defined in 3GPP TS 27.007 section
+ 10.1.19. See the MM_MODEM_GSM_NETWORK_REG_STATUS enumeration for
+ possible values.
+ </li>
+ <li>
+ Current operator code of the operator to which the mobile is
+ currently registered. Returned in the format "MCCMNC", where MCC
+ is the three-digit ITU E.212 Mobile Country Code and MNC is the
+ two- or three-digit GSM Mobile Network Code. If the MCC and MNC
+ are not known or the mobile is not registered to a mobile network,
+ this value should be a zero-length (blank) string. e.g. "31026"
+ or "310260".
+ </li>
+ <li>
+ Current operator name of the operator to which the mobile is
+ currently registered. If the operator name is not knowon or the
+ mobile is not registered to a mobile network, this value should
+ be a zero-length (blank) string.
+ </li>
+ </ul>
</tp:docstring>
</arg>
</method>
+ <method name="SetAllowedMode">
+ <tp:docstring>
+ Set the access technologies a device is allowed to use when connecting
+ to a mobile network.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_gsm_modem_set_allowed_mode"/>
+ <arg name="mode" type="u" direction="in" tp:type="MM_MODEM_GSM_ALLOWED_MODE">
+ <tp:docstring>
+ The allowed mode. The device may not support all modes; see
+ the org.freedesktop.ModemManager.Gsm.Card.SupportedModes property for
+ allowed modes for each device. All devices support the "ANY" flag.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <property name="AllowedMode" type="u" access="read" tp:type="MM_MODEM_GSM_ALLOWED_MODE">
+ <tp:docstring>
+ The allowed access technologies (eg 2G/3G preference) the device is allowed
+ to use when connecting to a mobile network.
+ </tp:docstring>
+ </property>
+
+ <property name="AccessTechnology" type="u" access="read" tp:type="MM_MODEM_GSM_ACCESS_TECH">
+ <tp:docstring>
+ The current network access technology used by the device to communicate
+ with the base station. (Note to plugin writers: if the device's access
+ technology cannot be determined, use UNKNOWN)
+ </tp:docstring>
+ </property>
+
<signal name="SignalQuality">
<tp:docstring>
The signal quality changed.
@@ -139,33 +225,45 @@
</tp:docstring>
<arg name="status" type="u" tp:type="MM_MODEM_GSM_NETWORK_REG_STATUS">
<tp:docstring>
- The network status.
+ Mobile registration status as defined in 3GPP TS 27.007 section
+ 10.1.19.
</tp:docstring>
</arg>
<arg name="operator_code" type="s">
<tp:docstring>
- The current operator code.
+ Current operator code of the operator to which the mobile is
+ currently registered. Returned in the format "MCCMNC", where MCC
+ is the three-digit ITU E.212 Mobile Country Code and MNC is the
+ two- or three-digit GSM Mobile Network Code. If the MCC and MNC
+ are not known or the mobile is not registered to a mobile network,
+ this value should be a zero-length (blank) string. e.g. "31026" or
+ "310260".
</tp:docstring>
</arg>
<arg name="operator_name" type="s">
<tp:docstring>
- The current operator name.
+ Current operator name of the operator to which the mobile is
+ currently registered. If the operator name is not knowon or the
+ mobile is not registered to a mobile network, this value should
+ be a zero-length (blank) string.
</tp:docstring>
</arg>
</signal>
<signal name="NetworkMode">
<tp:docstring>
- The network mode changed.
+ The network mode preference changed. (DEPRECATED; see documentation
+ for GetNetworkMode/SetNetworkMode)
</tp:docstring>
- <arg name="mode" type="u" tp:type="MM_MODEM_GSM_MODE">
- <tp:docstring>
- The new network mode.
- </tp:docstring>
+ <arg name="mode" type="u" tp:type="MM_MODEM_GSM_NETWORK_DEPRECATED_MODE">
+ <tp:docstring>The new network mode.</tp:docstring>
</arg>
</signal>
<tp:enum name="MM_MODEM_GSM_NETWORK_REG_STATUS" type="u">
+ <tp:docstring>
+ GSM registration code as defined in 3GPP TS 27.007 section 10.1.19.
+ </tp:docstring>
<tp:enumvalue suffix="IDLE" value="0">
<tp:docstring>
Not registered, not searching for new operator to register.
@@ -198,5 +296,45 @@
</tp:enumvalue>
</tp:enum>
+ <tp:enum name="MM_MODEM_GSM_NETWORK_DEPRECATED_MODE" type="u">
+ <tp:docstring>
+ DEPRECATED; should not be used in new applications. Use
+ AccessTechnology, AllowedMode, and SetAllowedMode() instead.
+ </tp:docstring>
+ <tp:enumvalue suffix="ANY" value="0">
+ <tp:docstring>Any network mode can be used</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GPRS" value="1">
+ <tp:docstring>GPRS</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="EDGE" value="2">
+ <tp:docstring>EDGE</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="UMTS" value="3">
+ <tp:docstring>UMTS (3G)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSDPA" value="4">
+ <tp:docstring>HSDPA</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="2G_PREFERRED" value="5">
+ <tp:docstring>Prefer 2G (GPRS or EDGE)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="3G_PREFERRED" value="6">
+ <tp:docstring>Prefer 3G (UMTS/HSDPA/HSUPA/HSPA)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="2G_ONLY" value="7">
+ <tp:docstring>Use only 2G (GPRS or EDGE)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="3G_ONLY" value="8">
+ <tp:docstring>Use only 3G (UMTS/HSDPA/HSUPA/HSPA)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSUPA" value="9">
+ <tp:docstring>HSUPA</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSPA" value="10">
+ <tp:docstring>HSPA (HSDPA + HSUPA)</tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
</interface>
</node>
diff --git a/introspection/mm-modem-gsm-ussd.xml b/introspection/mm-modem-gsm-ussd.xml
new file mode 100644
index 0000000..ae6884f
--- /dev/null
+++ b/introspection/mm-modem-gsm-ussd.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+
+ <interface name="org.freedesktop.ModemManager.Modem.Gsm.Ussd">
+ <method name="Initiate">
+ <tp:docstring>
+ Sends a USSD command string to the network initiating a USSD session.
+ When the request is handled by the network, the method returns the
+ response or an appropriate error. The network may be awaiting further
+ response from the ME after returning from this method and no new command
+ can be initiated until this one is cancelled or ended.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_gsm_ussd_initiate"/>
+ <arg name="command" type="s" direction="in">
+ <tp:docstring>
+ The command to start the USSD session with.
+ </tp:docstring>
+ </arg>
+ <arg name="reply" type="s" direction="out">
+ <tp:docstring>
+ The network response to the command which started the USSD session.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Respond">
+ <tp:docstring>
+ Respond to a USSD request that is either initiated by the mobile network,
+ or that is awaiting further input after Initiate() was called.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_gsm_ussd_respond"/>
+ <arg name="response" type="s" direction="in">
+ <tp:docstring>
+ The response to network-initiated USSD command, or a response to a
+ request for further input.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Cancel">
+ <tp:docstring>
+ Cancel an ongoing USSD session, either mobile or network initiated.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_gsm_ussd_cancel"/>
+ </method>
+
+ <property name="State" type="s" access="read">
+ <tp:docstring>
+ Indicates the state of any ongoing USSD session. Values may be one of
+ the following: "idle" (no active session), "active" (a session is active
+ and the mobile is waiting for a response), "user-response" (the network
+ is waiting for the client's response, which must be sent using Respond()).
+ </tp:docstring>
+ </property>
+
+ <property name="NetworkNotification" type="s" access="read">
+ <tp:docstring>
+ Contains any network-initiated request to which no USSD response is
+ required. When no USSD session is active, or when there is no network-
+ initiated request, this property will be a zero-length string.
+ </tp:docstring>
+ </property>
+
+ <property name="NetworkRequest" type="s" access="read">
+ <tp:docstring>
+ Contains any pending network-initiated request for a response. Client
+ should call Respond() with the appropriate response to this request.
+ When no USSD session is active, or when there is no pending
+ network-initiated request, this property will be a zero-length string.
+ </tp:docstring>
+ </property>
+
+ </interface>
+</node>
diff --git a/introspection/mm-modem-gsm.xml b/introspection/mm-modem-gsm.xml
index d6c42c8..ea23adc 100644
--- a/introspection/mm-modem-gsm.xml
+++ b/introspection/mm-modem-gsm.xml
@@ -3,6 +3,11 @@
<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
<interface name="org.freedesktop.ModemManager.Modem.Gsm">
<tp:flags name="MM_MODEM_GSM_MODE" value-prefix="MM_MODEM_GSM_MODE" type="u">
+ <tp:docstring>
+ A bitfield describing the specifc access modes and technologies
+ supported by a device and the access technology in-use when connected to
+ a mobile network.
+ </tp:docstring>
<tp:flag suffix="UNKNOWN" value="0x0">
<tp:docstring>Unknown or invalid mode.</tp:docstring>
</tp:flag>
@@ -19,28 +24,39 @@
<tp:docstring>UMTS (3G)</tp:docstring>
</tp:flag>
<tp:flag suffix="HSDPA" value="0x10">
- <tp:docstring>HSDPA</tp:docstring>
+ <tp:docstring>HSDPA (3G)</tp:docstring>
</tp:flag>
<tp:flag suffix="2G_PREFERRED" value="0x20">
<tp:docstring>Prefer 2G (GPRS or EDGE)</tp:docstring>
</tp:flag>
<tp:flag suffix="3G_PREFERRED" value="0x40">
- <tp:docstring>Prefer 3G (UMTS or HSDPA)</tp:docstring>
+ <tp:docstring>Prefer 3G (UMTS/HSDPA/HSUPA/HSPA)</tp:docstring>
</tp:flag>
<tp:flag suffix="2G_ONLY" value="0x80">
<tp:docstring>Use only 2G (GPRS or EDGE)</tp:docstring>
</tp:flag>
<tp:flag suffix="3G_ONLY" value="0x100">
- <tp:docstring>Use only 3G (UMTS or HSDPA)</tp:docstring>
+ <tp:docstring>Use only 3G (UMTS/HSDPA/HSUPA/HSPA)</tp:docstring>
</tp:flag>
<tp:flag suffix="HSUPA" value="0x200">
- <tp:docstring>HSUPA</tp:docstring>
+ <tp:docstring>HSUPA (3G)</tp:docstring>
+ </tp:flag>
+ <tp:flag suffix="HSPA" value="0x400">
+ <tp:docstring>HSPA (3G)</tp:docstring>
+ </tp:flag>
+ <tp:flag suffix="GSM" value="0x800">
+ <tp:docstring>GSM</tp:docstring>
+ </tp:flag>
+ <tp:flag suffix="GSM_COMPACT" value="0x1000">
+ <tp:docstring>GSM Compact</tp:docstring>
</tp:flag>
</tp:flags>
<tp:flags name="MM_MODEM_GSM_BAND" value-prefix="MM_MODEM_GSM_BAND" type="u">
<tp:docstring>
- GSM network and device bands.
+ A bitfield describing the specific radio bands supported by the device
+ and the radio bands the device is allowed to use when connecting to a
+ mobile network.
</tp:docstring>
<tp:flag suffix="UNKNOWN" value="0x0">
<tp:docstring>Unknown or invalid band</tp:docstring>
@@ -81,6 +97,68 @@
<tp:flag suffix="U17IX" value="0x800">
<tp:docstring>WCDMA 3GPP UMTS 1700 MHz (Class IX)</tp:docstring>
</tp:flag>
+ <tp:flag suffix="U1900" value="0x1000">
+ <tp:docstring>WCDMA 3GPP UMTS 1900 MHz (Class II)</tp:docstring>
+ </tp:flag>
</tp:flags>
+
+ <tp:enum name="MM_MODEM_GSM_ALLOWED_MODE" type="u">
+ <tp:docstring>
+ Describes the device's current access mode preference; ie the specific
+ technology preferences the device is allowed to use when connecting to
+ a mobile network.
+ </tp:docstring>
+ <tp:enumvalue suffix="ANY" value="0">
+ <tp:docstring>Any mode can be used</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="2G_PREFERRED" value="1">
+ <tp:docstring>Prefer 2G (GPRS or EDGE)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="3G_PREFERRED" value="2">
+ <tp:docstring>Prefer 3G (UMTS or HSxPA)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="2G_ONLY" value="3">
+ <tp:docstring>Use only 2G (GPRS or EDGE)</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="3G_ONLY" value="4">
+ <tp:docstring>Use only 3G (UMTS or HSxPA)</tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <tp:enum name="MM_MODEM_GSM_ACCESS_TECH" type="u">
+ <tp:docstring>
+ Describes various access technologies that a device uses when connected
+ to a mobile network.
+ </tp:docstring>
+ <tp:enumvalue suffix="UNKNOWN" value="0">
+ <tp:docstring>The access technology used is unknown</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GSM" value="1">
+ <tp:docstring>GSM</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GSM_COMPACT" value="2">
+ <tp:docstring>Compact GSM</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="GPRS" value="3">
+ <tp:docstring>GPRS</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="EDGE" value="4">
+ <tp:docstring>EDGE (ETSI 27.007: "GSM w/EGPRS")</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="UMTS" value="5">
+ <tp:docstring>UMTS (ETSI 27.007: "UTRAN")</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSDPA" value="6">
+ <tp:docstring>HSDPA (ETSI 27.007: "UTRAN w/HSDPA")</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSUPA" value="7">
+ <tp:docstring>HSUPA (ETSI 27.007: "UTRAN w/HSUPA")</tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="HSPA" value="8">
+ <tp:docstring>HSPA (ETSI 27.007: "UTRAN w/HSDPA and HSUPA")</tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
</interface>
</node>
+
diff --git a/introspection/mm-modem-location.xml b/introspection/mm-modem-location.xml
new file mode 100644
index 0000000..58dca68
--- /dev/null
+++ b/introspection/mm-modem-location.xml
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.freedesktop.ModemManager.Modem.Location">
+ <tp:docstring>
+ This interface allows devices to provide location information to client
+ applications. Not all devices can provide this information, or even if
+ they do, they may not be able to provide it while a data session is
+ active.
+ </tp:docstring>
+
+ <property name="Capabilities" type="u" access="read" tp:type="MM_MODEM_LOCATION_CAPABILITIES">
+ <tp:docstring>
+ Location capabilities of the device.
+ </tp:docstring>
+ </property>
+
+ <property name="Enabled" type="b" access="read">
+ <tp:docstring>
+ TRUE if location information gathering is enabled for this device, FALSE
+ if it is disabled. When disabled, the device will not provide location
+ information.
+ </tp:docstring>
+ </property>
+
+ <method name="Enable">
+ <tp:docstring>
+ Enable or disable location information gathering. This method may
+ require the client to authenticate itself. This method may also cause
+ any necessary functionality of the mobile be be turned on, including
+ enabling the modem device itself.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_location_enable"/>
+ <arg name="enable" type="b" direction="in">
+ <tp:docstring>
+ TRUE to enable location information gathering, FALSE to disable.
+ </tp:docstring>
+ </arg>
+ <arg name="signal_location" type="b" direction="in">
+ <tp:docstring>
+ When enabling location information gathering, this argument controls
+ whether the device emits signals with new location information or not.
+ When signals are emitted, any client application (including malicious
+ ones!) can listen for location updates unless D-Bus permissions
+ restrict these signals from certain users. If further security is
+ desired, this argument can be set to FALSE to disable location
+ updates via D-Bus signals and require applications to call
+ authenticated APIs (like GetLocation) to get location information.
+ This argument is ignored when disabling location information
+ gathering.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetLocation">
+ <tp:docstring>
+ Return current location information, if any. This method may require
+ the client to authenticate itself.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_location_get_location"/>
+ <arg name="Location" type="a{uv}" direction="out" tp:type="Location_Information_Map">
+ <tp:docstring>
+ Dict of available location information when location information
+ gathering is enabled. If the modem supports multiple location types
+ it may return more than one here.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <property name="SignalsLocation" type="b" access="read">
+ <tp:docstring>
+ TRUE if location updates will be emitted via D-Bus signals, FALSE
+ if location updates will not be emitted. See the Enable method for
+ more information.
+ </tp:docstring>
+ </property>
+
+ <property name="Location" type="a{uv}" access="read" tp:type="Location_Information_Map">
+ <tp:docstring>
+ Dict of available location information when location information
+ gathering is enabled. If the modem supports multiple location types
+ it may return more than one here. Note that if the device was told
+ not to emit updated location information when location information
+ gathering was initially enabled, this property may not return
+ any location information for security reasons.
+ </tp:docstring>
+ </property>
+
+ <tp:mapping name="Location_Information_Map">
+ <tp:docstring>
+ A mapping from location type to type-specific location information.
+ </tp:docstring>
+ <tp:member type="u" name="Type" tp:type="MM_MODEM_LOCATION_CAPABILITIES">
+ <tp:docstring>
+ Identifies the type and format of the associated location information.
+ Contrary to the value description, this is not a bitfield but uses the
+ same values as the MM_MODEM_LOCATION_CAPABILITIES bitfield.
+ </tp:docstring>
+ </tp:member>
+ <tp:member type="v" name="Data">
+ <tp:docstring>
+ Contains type-specific location information. See the documentation for
+ each type for a description of its data format.
+ </tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ <tp:flags name="MM_MODEM_LOCATION_CAPABILITIES" value-prefix="MM_MODEM_LOCATION_CAPABILITY" type="u">
+ <tp:flag suffix="UNKNOWN" value="0x0">
+ <tp:docstring><p>Unknown or no capabilties.</p></tp:docstring>
+ </tp:flag>
+ <tp:flag suffix="GPS_NMEA" value="0x1">
+ <tp:docstring>
+ <p>For capability reporting, indicates the device is capable of
+ providing GPS NMEA-format location information.</p>
+
+ <p>For location reporting, devices supporting this capability return
+ a string containing one or more NMEA sentences (D-Bus signature 's').
+ The manager will cache the most recent NMEA sentence of each type for
+ a period of time not less than 30 seconds. When reporting multiple
+ NMEA sentences, sentences shall be separated by an ASCII Carriage
+ Return and Line Feed (&lt;CR&gt;&lt;LF&gt;) sequence.
+ </p>
+ <p>
+ For example, if the device sends a $GPRMC sentence immediately
+ followed by a $GPGGA sentence, the reported location string would be
+ (where of course the &lt;CR&gt;&lt;LF&gt; is replaced with the actual
+ ASCII CR (0x0D) and LF (0x0A) control characters):
+ <pre>
+ $GPRMC,134523.92,V,,,,,,,030136,,,N*73&lt;CR&gt;&lt;LF&gt;$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45
+ </pre>
+ If the device sends a new $GPRMC three seconds later, the new $GPRMC
+ replaces the previously received $GPRMC sentence, and the updated
+ string would be:
+ <pre>
+ $GPRMC,134526.92,V,,,,,,,030136,,,N*76&lt;CR&gt;&lt;LF&gt;$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45
+ </pre>
+ If the device then sends a $GPGSA sentence about 5 seconds later, the
+ $GPGSA sentence is added to the string (since no $GPGSA sentence was
+ previously received in this session), the updated string would be:
+ <pre>
+ $GPRMC,134526.92,V,,,,,,,030136,,,N*76&lt;CR&gt;&lt;LF&gt;$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45&lt;CR&gt;&lt;LF&gt;$GPGSA,A,1,,,,,,,,,,,,,1.1,0.5,1.0*34
+ </pre>
+ The manager may discard any cached sentences older than 30 seconds.
+ </p>
+ <p>This allows clients to read the latest positioning data as soon as
+ possible after they start, even if the device is not providing
+ frequent location data updates.
+ </p>
+ </tp:docstring>
+ </tp:flag>
+ <tp:flag suffix="GSM_LAC_CI" value="0x2">
+ <tp:docstring>
+ <p>For capability reporting, indicates the device is capable of
+ providing GSM Location Area Code/Cell ID location information.</p>
+
+ <p>For location reporting, devices supporting this
+ capability return a string in the format "MCC,MNC,LAC,CI" (without the
+ quotes of course) where the following applies:</p>
+ <ul>
+ <li>
+ MCC is the three-digit ITU E.212 Mobile Country Code of the
+ network provider to which the mobile is currently registered.
+ This value should be the same MCC as reported by the
+ org.freedesktop.Modem.Gsm.Network.GetRegistrationInfo() method's
+ returned "operator code" argument.
+ e.g. "310"
+ </li>
+ <li>
+ MNC is the two- or three-digit GSM Mobile Network Code of the
+ network provider to which the mobile is currently registered.
+ This value should be the same MCC as reported by the
+ org.freedesktop.Modem.Gsm.Network.GetRegistrationInfo() method's
+ returned "operator code" argument.
+ e.g. "26" or "260"
+ </li>
+ <li>
+ LAC is the two-byte Location Area Code of the base station with
+ which the mobile is registered, in upper-case hexadecimal format
+ without leading zeros, as specified in 3GPP TS 27.007 section
+ 10.1.19. e.g. "84CD".
+ </li>
+ <li>
+ CI is the two- or four-byte Cell Identifier with which the mobile
+ is registered, in upper-case hexadecimal format without leading
+ zeros, as specified in 3GPP TS 27.007. e.g. "2BAF" or "D30156".
+ </li>
+ </ul>
+ <p>The entire string may only be composed of the ASCII digits [0-9],
+ the alphabetical characters [A-F], and the comma (,) character. No
+ other characters are allowed. For example: "310,260,8BE3,2BAF" or
+ "250,40,CE00,1CEAD8F0".</p>
+
+ <p>If any of these four items (MCC,MNC,LAC,CI) is unknown or the
+ mobile is not registered with a network, then the GSM_LAC_CI location
+ information item should not be provided as a returned value from the
+ GetLocation() method or in the Location property.</p>
+ </tp:docstring>
+ </tp:flag>
+ <tp:flag suffix="GSM_GPS_RAW" value="0x4">
+ <tp:docstring>
+ <p>For capability reporting, indicates the device is capable of
+ providing raw GPS information using a series of defined key/value
+ pairs.</p>
+
+ <p>For location reporting, devices supporting this
+ capability return a D-Bus dict (signature a{sv}) mapping well-known
+ keys to values with defined formats. The allowed key/value pairs
+ and their formats are:</p>
+ <table>
+ <tr><th>Key</th><th>Value Type</th><th>Value contents</th><th>Example</th></tr>
+ <tr>
+ <td>latitude</td>
+ <td>d</td>
+ <td>Latitude in Decimal Degrees (positive numbers mean N quadrasphere, negative mean S quadrasphere)</td>
+ <td>38.889722 (ie, 38d 53' 22" N)</td>
+ </tr>
+ <tr>
+ <td>longitude</td>
+ <td>d</td>
+ <td>Longitude in Decimal Degrees (positive numbers mean E quadrasphere, negative mean W quadrasphere)</td>
+ <td>-77.008889 (ie, 77d 0' 32" W)</td>
+ </tr>
+ <tr>
+ <td>altitude</td>
+ <td>d</td>
+ <td>Altitude above sea level in meters</td>
+ <td>33.5</td>
+ </tr>
+ <tr>
+ <td>horiz-velocity</td>
+ <td>d</td>
+ <td>Horizontal velocity in meters-per-second</td>
+ <td>.5</td>
+ </tr>
+ <tr>
+ <td>vert-velocity</td>
+ <td>d</td>
+ <td>Vertical velocity in meters-per-second</td>
+ <td>.01</td>
+ </tr>
+ </table>
+ <p>The 'latitude' and 'longitude' keys are required; other keys are
+ optional.</p>
+ </tp:docstring>
+ </tp:flag>
+ </tp:flags>
+
+ </interface>
+</node>
+
diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml
index 7896fff..7d54dd3 100644
--- a/introspection/mm-modem.xml
+++ b/introspection/mm-modem.xml
@@ -81,6 +81,17 @@
</arg>
</method>
+ <method name="FactoryReset">
+ <tp:docstring>
+ Reset the modem to as close to factory state as possible.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_modem_factory_reset"/>
+ <arg name="code" type="s" direction="in">
+ Carrier-supplied code required to reset the modem. Ignored if not required.
+ </arg>
+ </method>
+
<property name="Device" type="s" access="read">
<tp:docstring>
The modem port to use for IP configuration and traffic.
@@ -113,6 +124,32 @@
</tp:docstring>
</property>
+ <property name="EquipmentIdentifier" type="s" access="read">
+ <tp:docstring>
+ The identity of the device. This will be the IMEI number for
+ GSM devices and the hex-format ESN/MEID for CDMA devices.
+ </tp:docstring>
+ </property>
+
+ <property name="UnlockRequired" type="s" access="read">
+ <tp:docstring>
+ Empty if the device is usable without an unlock code or has already
+ been unlocked. If the device needs to be unlocked before becoming usable this
+ property contains the specific unlock code required.  Valid unlock code values
+ are "" (blank), "sim-pin", "sim-puk", "ph-sim-pin", "ph-fsim-pin",
+ "ph-fsim-puk", "sim-pin2", "sim-puk2", "ph-net-pin", "ph-net-puk",
+ "ph-netsub-pin", "ph-netsub-puk", "ph-sp-pin", "ph-sp-puk", "ph-corp-pin", and
+ "ph-corp-puk".
+ </tp:docstring>
+ </property>
+
+ <property name="UnlockRetries" type="u" access="read">
+ <tp:docstring>
+ The number of unlock retries remaining for the unlock code given by the property UnlockRequired, or 999 if
+ the device does not support reporting unlock retries.
+ </tp:docstring>
+ </property>
+
<property name="IpMethod" type="u" access="read" tp:type="MM_MODEM_IP_METHOD">
<tp:docstring>
The IP configuration method.
diff --git a/libqcdm/Makefile.am b/libqcdm/Makefile.am
new file mode 100644
index 0000000..77f28d7
--- /dev/null
+++ b/libqcdm/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS=src tests
+
diff --git a/libqcdm/src/Makefile.am b/libqcdm/src/Makefile.am
new file mode 100644
index 0000000..5cc0568
--- /dev/null
+++ b/libqcdm/src/Makefile.am
@@ -0,0 +1,42 @@
+noinst_LTLIBRARIES = libqcdm.la libqcdm-test.la
+
+
+libqcdm_la_CPPFLAGS = \
+ $(MM_CFLAGS)
+
+libqcdm_la_SOURCES = \
+ dm-commands.h \
+ nv-items.h \
+ com.c \
+ com.h \
+ commands.c \
+ commands.h \
+ error.c \
+ error.h \
+ result.c \
+ result.h \
+ result-private.h \
+ utils.c \
+ utils.h
+
+libqcdm_la_LIBADD = \
+ $(MM_LIBS)
+
+libqcdm_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libqcdm.ver \
+ -version-info "0:0:0"
+
+
+###########################################
+# Test library without symbol versioning
+###########################################
+
+libqcdm_test_la_CPPFLAGS = \
+ $(MM_CFLAGS)
+
+libqcdm_test_la_SOURCES = \
+ utils.c \
+ utils.h
+
+libqcdm_test_la_LIBADD = \
+ $(MM_LIBS)
+
diff --git a/libqcdm/src/com.c b/libqcdm/src/com.c
new file mode 100644
index 0000000..353103a
--- /dev/null
+++ b/libqcdm/src/com.c
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "com.h"
+#include "error.h"
+
+gboolean
+qcdm_port_setup (int fd, GError **error)
+{
+ struct termios stbuf;
+
+ g_type_init ();
+
+ errno = 0;
+ memset (&stbuf, 0, sizeof (stbuf));
+ if (tcgetattr (fd, &stbuf) != 0) {
+ g_set_error (error,
+ QCDM_SERIAL_ERROR, QCDM_SERIAL_CONFIG_FAILED,
+ "tcgetattr() error: %d", errno);
+ }
+
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
+ stbuf.c_iflag &= ~(HUPCL | IUTF8 | IUCLC | ISTRIP | IXON | IXOFF | IXANY | ICRNL);
+ stbuf.c_oflag &= ~(OPOST | OCRNL | ONLCR | OLCUC | ONLRET);
+ stbuf.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL);
+ stbuf.c_lflag &= ~(NOFLSH | XCASE | TOSTOP | ECHOPRT | ECHOCTL | ECHOKE);
+ stbuf.c_cc[VMIN] = 1;
+ stbuf.c_cc[VTIME] = 0;
+ stbuf.c_cc[VEOF] = 1;
+ stbuf.c_cflag |= (B115200 | CS8 | CREAD | 0 | 0); /* No parity, 1 stop bit */
+
+ errno = 0;
+ if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
+ g_set_error (error,
+ QCDM_SERIAL_ERROR, QCDM_SERIAL_CONFIG_FAILED,
+ "tcsetattr() error: %d", errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/libqcdm/src/com.h b/libqcdm/src/com.h
new file mode 100644
index 0000000..97561d0
--- /dev/null
+++ b/libqcdm/src/com.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_COM_H
+#define LIBQCDM_COM_H
+
+#include <glib.h>
+
+gboolean qcdm_port_setup (int fd, GError **error);
+
+#endif /* LIBQCDM_COM_H */
diff --git a/libqcdm/src/commands.c b/libqcdm/src/commands.c
new file mode 100644
index 0000000..11a1a38
--- /dev/null
+++ b/libqcdm/src/commands.c
@@ -0,0 +1,1064 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "commands.h"
+#include "error.h"
+#include "dm-commands.h"
+#include "nv-items.h"
+#include "result-private.h"
+#include "utils.h"
+
+
+/**********************************************************************/
+
+static guint8
+cdma_prev_to_qcdm (guint8 cdma)
+{
+ switch (cdma) {
+ case CDMA_PREV_IS_95:
+ return QCDM_CDMA_PREV_IS_95;
+ case CDMA_PREV_IS_95A:
+ return QCDM_CDMA_PREV_IS_95A;
+ case CDMA_PREV_IS_95A_TSB74:
+ return QCDM_CDMA_PREV_IS_95A_TSB74;
+ case CDMA_PREV_IS_95B_PHASE1:
+ return QCDM_CDMA_PREV_IS_95B_PHASE1;
+ case CDMA_PREV_IS_95B_PHASE2:
+ return QCDM_CDMA_PREV_IS_95B_PHASE2;
+ case CDMA_PREV_IS2000_REL0:
+ return QCDM_CDMA_PREV_IS2000_REL0;
+ case CDMA_PREV_IS2000_RELA:
+ return QCDM_CDMA_PREV_IS2000_RELA;
+ default:
+ break;
+ }
+ return QCDM_CDMA_PREV_UNKNOWN;
+}
+
+static guint8
+cdma_band_class_to_qcdm (guint8 cdma)
+{
+ switch (cdma) {
+ case CDMA_BAND_CLASS_0_CELLULAR_800:
+ return QCDM_CDMA_BAND_CLASS_0_CELLULAR_800;
+ case CDMA_BAND_CLASS_1_PCS:
+ return QCDM_CDMA_BAND_CLASS_1_PCS;
+ case CDMA_BAND_CLASS_2_TACS:
+ return QCDM_CDMA_BAND_CLASS_2_TACS;
+ case CDMA_BAND_CLASS_3_JTACS:
+ return QCDM_CDMA_BAND_CLASS_3_JTACS;
+ case CDMA_BAND_CLASS_4_KOREAN_PCS:
+ return QCDM_CDMA_BAND_CLASS_4_KOREAN_PCS;
+ case CDMA_BAND_CLASS_5_NMT450:
+ return QCDM_CDMA_BAND_CLASS_5_NMT450;
+ case CDMA_BAND_CLASS_6_IMT2000:
+ return QCDM_CDMA_BAND_CLASS_6_IMT2000;
+ case CDMA_BAND_CLASS_7_CELLULAR_700:
+ return QCDM_CDMA_BAND_CLASS_7_CELLULAR_700;
+ case CDMA_BAND_CLASS_8_1800:
+ return QCDM_CDMA_BAND_CLASS_8_1800;
+ case CDMA_BAND_CLASS_9_900:
+ return QCDM_CDMA_BAND_CLASS_9_900;
+ case CDMA_BAND_CLASS_10_SECONDARY_800:
+ return QCDM_CDMA_BAND_CLASS_10_SECONDARY_800;
+ case CDMA_BAND_CLASS_11_PAMR_400:
+ return QCDM_CDMA_BAND_CLASS_11_PAMR_400;
+ case CDMA_BAND_CLASS_12_PAMR_800:
+ return QCDM_CDMA_BAND_CLASS_12_PAMR_800;
+ default:
+ break;
+ }
+ return QCDM_CDMA_BAND_CLASS_UNKNOWN;
+}
+
+/**********************************************************************/
+
+/*
+ * utils_bin2hexstr
+ *
+ * Convert a byte-array into a hexadecimal string.
+ *
+ * Code originally by Alex Larsson <alexl@redhat.com> and
+ * copyright Red Hat, Inc. under terms of the LGPL.
+ *
+ */
+static char *
+bin2hexstr (const guint8 *bytes, int len)
+{
+ static char hex_digits[] = "0123456789abcdef";
+ char *result;
+ int i;
+ gsize buflen = (len * 2) + 1;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (len > 0, NULL);
+ g_return_val_if_fail (len < 4096, NULL); /* Arbitrary limit */
+
+ result = g_malloc0 (buflen);
+ for (i = 0; i < len; i++) {
+ result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf];
+ result[2*i+1] = hex_digits[bytes[i] & 0xf];
+ }
+ result[buflen - 1] = '\0';
+ return result;
+}
+
+static gboolean
+check_command (const char *buf, gsize len, guint8 cmd, gsize min_len, GError **error)
+{
+ if (len < 1) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_MALFORMED_RESPONSE,
+ "DM command response malformed (must be at least 1 byte in length)");
+ return FALSE;
+ }
+
+ switch (buf[0]) {
+ case DIAG_CMD_BAD_CMD:
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND,
+ "DM command %d unknown or unimplemented by the device",
+ cmd);
+ return FALSE;
+ case DIAG_CMD_BAD_PARM:
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "DM command %d contained invalid parameter",
+ cmd);
+ return FALSE;
+ case DIAG_CMD_BAD_LEN:
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_LENGTH,
+ "DM command %d was the wrong size",
+ cmd);
+ return FALSE;
+ case DIAG_CMD_BAD_DEV:
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_NOT_ACCEPTED,
+ "DM command %d was not accepted by the device",
+ cmd);
+ return FALSE;
+ case DIAG_CMD_BAD_MODE:
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_MODE,
+ "DM command %d not allowed in the current device mode",
+ cmd);
+ return FALSE;
+ default:
+ break;
+ }
+
+ if (buf[0] != cmd) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_UNEXPECTED,
+ "Unexpected DM command response (expected %d, got %d)",
+ cmd, buf[0]);
+ return FALSE;
+ }
+
+ if (len < min_len) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_LENGTH,
+ "DM command %d response not long enough (got %zu, expected "
+ "at least %zu).", cmd, len, min_len);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_nv_cmd (DMCmdNVReadWrite *cmd, guint16 nv_item, GError **error)
+{
+ guint16 cmd_item;
+
+ g_return_val_if_fail (cmd != NULL, FALSE);
+ g_return_val_if_fail ((cmd->code == DIAG_CMD_NV_READ) || (cmd->code == DIAG_CMD_NV_WRITE), FALSE);
+
+ /* NV read/write have a status byte at the end */
+ if (cmd->status != 0) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_NVCMD_FAILED,
+ "The NV operation failed (status 0x%X).",
+ GUINT16_FROM_LE (cmd->status));
+ return FALSE;
+ }
+
+ cmd_item = GUINT16_FROM_LE (cmd->nv_item);
+ if (cmd_item != nv_item) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_UNEXPECTED,
+ "Unexpected DM NV command response (expected item %d, got "
+ "item %d)", nv_item, cmd_item);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_version_info_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[3];
+ DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_VERSION_INFO;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_version_info_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdVersionInfoRsp *rsp = (DMCmdVersionInfoRsp *) buf;
+ char tmp[12];
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_VERSION_INFO, sizeof (DMCmdVersionInfoRsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->comp_date) <= sizeof (tmp));
+ memcpy (tmp, rsp->comp_date, sizeof (rsp->comp_date));
+ qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_DATE, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->comp_time) <= sizeof (tmp));
+ memcpy (tmp, rsp->comp_time, sizeof (rsp->comp_time));
+ qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_TIME, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->rel_date) <= sizeof (tmp));
+ memcpy (tmp, rsp->rel_date, sizeof (rsp->rel_date));
+ qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_DATE, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->rel_time) <= sizeof (tmp));
+ memcpy (tmp, rsp->rel_time, sizeof (rsp->rel_time));
+ qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_TIME, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->model) <= sizeof (tmp));
+ memcpy (tmp, rsp->model, sizeof (rsp->model));
+ qcdm_result_add_string (result, QCDM_CMD_VERSION_INFO_ITEM_MODEL, tmp);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_esn_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[3];
+ DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_ESN;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_esn_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdEsnRsp *rsp = (DMCmdEsnRsp *) buf;
+ char *tmp;
+ guint8 swapped[4];
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_ESN, sizeof (DMCmdEsnRsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ /* Convert the ESN from binary to a hex string; it's LE so we have to
+ * swap it to get the correct ordering.
+ */
+ swapped[0] = rsp->esn[3];
+ swapped[1] = rsp->esn[2];
+ swapped[2] = rsp->esn[1];
+ swapped[3] = rsp->esn[0];
+
+ tmp = bin2hexstr (&swapped[0], sizeof (swapped));
+ qcdm_result_add_string (result, QCDM_CMD_ESN_ITEM_ESN, tmp);
+ g_free (tmp);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_cdma_status_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[3];
+ DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_STATUS;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_cdma_status_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdStatusRsp *rsp = (DMCmdStatusRsp *) buf;
+ char *tmp;
+ guint8 swapped[4];
+ guint32 tmp_num;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_STATUS, sizeof (DMCmdStatusRsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ /* Convert the ESN from binary to a hex string; it's LE so we have to
+ * swap it to get the correct ordering.
+ */
+ swapped[0] = rsp->esn[3];
+ swapped[1] = rsp->esn[2];
+ swapped[2] = rsp->esn[1];
+ swapped[3] = rsp->esn[0];
+
+ tmp = bin2hexstr (&swapped[0], sizeof (swapped));
+ qcdm_result_add_string (result, QCDM_CMD_CDMA_STATUS_ITEM_ESN, tmp);
+ g_free (tmp);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->rf_mode);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE, tmp_num);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->cdma_rx_state);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, tmp_num);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->entry_reason);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON, tmp_num);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->curr_chan);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL, tmp_num);
+
+ qcdm_result_add_uint8 (result, QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL, rsp->cdma_code_chan);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->pilot_base);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE, tmp_num);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->sid);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, tmp_num);
+
+ tmp_num = (guint32) GUINT16_FROM_LE (rsp->nid);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_NID, tmp_num);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_sw_version_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[3];
+ DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_SW_VERSION;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_sw_version_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdSwVersionRsp *rsp = (DMCmdSwVersionRsp *) buf;
+ char tmp[25];
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_SW_VERSION, sizeof (*rsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->version) <= sizeof (tmp));
+ memcpy (tmp, rsp->version, sizeof (rsp->version));
+ qcdm_result_add_string (result, QCDM_CMD_SW_VERSION_ITEM_VERSION, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->comp_date) <= sizeof (tmp));
+ memcpy (tmp, rsp->comp_date, sizeof (rsp->comp_date));
+ qcdm_result_add_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_DATE, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (rsp->comp_time) <= sizeof (tmp));
+ memcpy (tmp, rsp->comp_time, sizeof (rsp->comp_time));
+ qcdm_result_add_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_TIME, tmp);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_pilot_sets_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[3];
+ DMCmdHeader *cmd = (DMCmdHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_PILOT_SETS;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+#define PILOT_SETS_CMD_ACTIVE_SET "active-set"
+#define PILOT_SETS_CMD_CANDIDATE_SET "candidate-set"
+#define PILOT_SETS_CMD_NEIGHBOR_SET "neighbor-set"
+
+static const char *
+set_num_to_str (guint32 num)
+{
+ if (num == QCDM_CMD_PILOT_SETS_TYPE_ACTIVE)
+ return PILOT_SETS_CMD_ACTIVE_SET;
+ if (num == QCDM_CMD_PILOT_SETS_TYPE_CANDIDATE)
+ return PILOT_SETS_CMD_CANDIDATE_SET;
+ if (num == QCDM_CMD_PILOT_SETS_TYPE_NEIGHBOR)
+ return PILOT_SETS_CMD_NEIGHBOR_SET;
+ return NULL;
+}
+
+QCDMResult *
+qcdm_cmd_pilot_sets_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdPilotSetsRsp *rsp = (DMCmdPilotSetsRsp *) buf;
+ GByteArray *array;
+ gsize sets_len;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_PILOT_SETS, sizeof (DMCmdPilotSetsRsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ sets_len = rsp->active_count * sizeof (DMCmdPilotSetsSet);
+ if (sets_len > 0) {
+ array = g_byte_array_sized_new (sets_len);
+ g_byte_array_append (array, (const guint8 *) &rsp->sets[0], sets_len);
+ qcdm_result_add_boxed (result, PILOT_SETS_CMD_ACTIVE_SET, G_TYPE_BYTE_ARRAY, array);
+ }
+
+ sets_len = rsp->candidate_count * sizeof (DMCmdPilotSetsSet);
+ if (sets_len > 0) {
+ array = g_byte_array_sized_new (sets_len);
+ g_byte_array_append (array, (const guint8 *) &rsp->sets[rsp->active_count], sets_len);
+ qcdm_result_add_boxed (result, PILOT_SETS_CMD_CANDIDATE_SET, G_TYPE_BYTE_ARRAY, array);
+ }
+
+ sets_len = rsp->neighbor_count * sizeof (DMCmdPilotSetsSet);
+ if (sets_len > 0) {
+ array = g_byte_array_sized_new (sets_len);
+ g_byte_array_append (array, (const guint8 *) &rsp->sets[rsp->active_count + rsp->candidate_count], sets_len);
+ qcdm_result_add_boxed (result, PILOT_SETS_CMD_NEIGHBOR_SET, G_TYPE_BYTE_ARRAY, array);
+ }
+
+ return result;
+}
+
+gboolean
+qcdm_cmd_pilot_sets_result_get_num (QCDMResult *result,
+ guint32 set_type,
+ guint32 *out_num)
+{
+ const char *set_name;
+ GByteArray *array = NULL;
+
+ g_return_val_if_fail (result != NULL, FALSE);
+
+ set_name = set_num_to_str (set_type);
+ g_return_val_if_fail (set_name != NULL, FALSE);
+
+ if (!qcdm_result_get_boxed (result, set_name, (gpointer) &array))
+ return FALSE;
+
+ *out_num = array->len / sizeof (DMCmdPilotSetsSet);
+ return TRUE;
+}
+
+gboolean
+qcdm_cmd_pilot_sets_result_get_pilot (QCDMResult *result,
+ guint32 set_type,
+ guint32 num,
+ guint32 *out_pn_offset,
+ guint32 *out_ecio,
+ float *out_db)
+{
+ const char *set_name;
+ GByteArray *array = NULL;
+ DMCmdPilotSetsSet *set;
+
+ g_return_val_if_fail (result != NULL, FALSE);
+
+ set_name = set_num_to_str (set_type);
+ g_return_val_if_fail (set_name != NULL, FALSE);
+
+ if (!qcdm_result_get_boxed (result, set_name, (gpointer) &array))
+ return FALSE;
+
+ g_return_val_if_fail (num < array->len / sizeof (DMCmdPilotSetsSet), FALSE);
+
+ set = (DMCmdPilotSetsSet *) &array->data[num * sizeof (DMCmdPilotSetsSet)];
+ *out_pn_offset = set->pn_offset;
+ *out_ecio = set->ecio;
+ /* EC/IO is in units of -0.5 dB per the specs */
+ *out_db = (float) set->ecio * -0.5;
+ return TRUE;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_nv_get_mdn_new (char *buf, gsize len, guint8 profile, GError **error)
+{
+ char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
+ DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
+ DMNVItemMdn *req;
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_NV_READ;
+ cmd->nv_item = GUINT16_TO_LE (DIAG_NV_DIR_NUMBER);
+
+ req = (DMNVItemMdn *) &cmd->data[0];
+ req->profile = profile;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_nv_get_mdn_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf;
+ DMNVItemMdn *mdn;
+ char tmp[11];
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), error))
+ return NULL;
+
+ if (!check_nv_cmd (rsp, DIAG_NV_DIR_NUMBER, error))
+ return NULL;
+
+ mdn = (DMNVItemMdn *) &rsp->data[0];
+
+ result = qcdm_result_new ();
+
+ qcdm_result_add_uint8 (result, QCDM_CMD_NV_GET_MDN_ITEM_PROFILE, mdn->profile);
+
+ memset (tmp, 0, sizeof (tmp));
+ g_assert (sizeof (mdn->mdn) <= sizeof (tmp));
+ memcpy (tmp, mdn->mdn, sizeof (mdn->mdn));
+ qcdm_result_add_string (result, QCDM_CMD_NV_GET_MDN_ITEM_MDN, tmp);
+
+ return result;
+}
+
+/**********************************************************************/
+
+static gboolean
+roam_pref_validate (guint8 dm)
+{
+ if ( dm == DIAG_NV_ROAM_PREF_HOME_ONLY
+ || dm == DIAG_NV_ROAM_PREF_ROAM_ONLY
+ || dm == DIAG_NV_ROAM_PREF_AUTO)
+ return TRUE;
+ return FALSE;
+}
+
+gsize
+qcdm_cmd_nv_get_roam_pref_new (char *buf, gsize len, guint8 profile, GError **error)
+{
+ char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
+ DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
+ DMNVItemRoamPref *req;
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_NV_READ;
+ cmd->nv_item = GUINT16_TO_LE (DIAG_NV_ROAM_PREF);
+
+ req = (DMNVItemRoamPref *) &cmd->data[0];
+ req->profile = profile;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_nv_get_roam_pref_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf;
+ DMNVItemRoamPref *roam;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), error))
+ return NULL;
+
+ if (!check_nv_cmd (rsp, DIAG_NV_ROAM_PREF, error))
+ return NULL;
+
+ roam = (DMNVItemRoamPref *) &rsp->data[0];
+
+ if (!roam_pref_validate (roam->roam_pref)) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "Unknown roam preference 0x%X",
+ roam->roam_pref);
+ return NULL;
+ }
+
+ result = qcdm_result_new ();
+ qcdm_result_add_uint8 (result, QCDM_CMD_NV_GET_ROAM_PREF_ITEM_PROFILE, roam->profile);
+ qcdm_result_add_uint8 (result, QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF, roam->roam_pref);
+
+ return result;
+}
+
+gsize
+qcdm_cmd_nv_set_roam_pref_new (char *buf,
+ gsize len,
+ guint8 profile,
+ guint8 roam_pref,
+ GError **error)
+{
+ char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
+ DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
+ DMNVItemRoamPref *req;
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ if (!roam_pref_validate (roam_pref)) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "Invalid roam preference %d", roam_pref);
+ return 0;
+ }
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_NV_WRITE;
+ cmd->nv_item = GUINT16_TO_LE (DIAG_NV_ROAM_PREF);
+
+ req = (DMNVItemRoamPref *) &cmd->data[0];
+ req->profile = profile;
+ req->roam_pref = roam_pref;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_nv_set_roam_pref_result (const char *buf, gsize len, GError **error)
+{
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), error))
+ return NULL;
+
+ if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_ROAM_PREF, error))
+ return NULL;
+
+ return qcdm_result_new ();
+}
+
+/**********************************************************************/
+
+static gboolean
+mode_pref_validate (guint8 dm)
+{
+ if ( dm == DIAG_NV_MODE_PREF_1X_ONLY
+ || dm == DIAG_NV_MODE_PREF_HDR_ONLY
+ || dm == DIAG_NV_MODE_PREF_AUTO)
+ return TRUE;
+ return FALSE;
+}
+
+gsize
+qcdm_cmd_nv_get_mode_pref_new (char *buf, gsize len, guint8 profile, GError **error)
+{
+ char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
+ DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
+ DMNVItemModePref *req;
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_NV_READ;
+ cmd->nv_item = GUINT16_TO_LE (DIAG_NV_MODE_PREF);
+
+ req = (DMNVItemModePref *) &cmd->data[0];
+ req->profile = profile;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_nv_get_mode_pref_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf;
+ DMNVItemModePref *mode;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_NV_READ, sizeof (DMCmdNVReadWrite), error))
+ return NULL;
+
+ if (!check_nv_cmd (rsp, DIAG_NV_MODE_PREF, error))
+ return NULL;
+
+ mode = (DMNVItemModePref *) &rsp->data[0];
+
+ if (!mode_pref_validate (mode->mode_pref)) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "Unknown mode preference 0x%X",
+ mode->mode_pref);
+ return NULL;
+ }
+
+ result = qcdm_result_new ();
+ qcdm_result_add_uint8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_PROFILE, mode->profile);
+ qcdm_result_add_uint8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, mode->mode_pref);
+
+ return result;
+}
+
+gsize
+qcdm_cmd_nv_set_mode_pref_new (char *buf,
+ gsize len,
+ guint8 profile,
+ guint8 mode_pref,
+ GError **error)
+{
+ char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
+ DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
+ DMNVItemModePref *req;
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ if (!mode_pref_validate (mode_pref)) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "Invalid mode preference %d", mode_pref);
+ return 0;
+ }
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_NV_WRITE;
+ cmd->nv_item = GUINT16_TO_LE (DIAG_NV_MODE_PREF);
+
+ req = (DMNVItemModePref *) &cmd->data[0];
+ req->profile = profile;
+ req->mode_pref = mode_pref;
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_nv_set_mode_pref_result (const char *buf, gsize len, GError **error)
+{
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_NV_WRITE, sizeof (DMCmdNVReadWrite), error))
+ return NULL;
+
+ if (!check_nv_cmd ((DMCmdNVReadWrite *) buf, DIAG_NV_MODE_PREF, error))
+ return NULL;
+
+ return qcdm_result_new ();
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_cm_subsys_state_info_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[sizeof (DMCmdSubsysHeader) + 2];
+ DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_SUBSYS;
+ cmd->subsys_id = DIAG_SUBSYS_CM;
+ cmd->subsys_cmd = GUINT16_TO_LE (DIAG_SUBSYS_CM_STATE_INFO);
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_cm_subsys_state_info_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdSubsysCMStateInfoRsp *rsp = (DMCmdSubsysCMStateInfoRsp *) buf;
+ guint32 tmp_num;
+ guint32 roam_pref;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysCMStateInfoRsp), error))
+ return NULL;
+
+ roam_pref = (guint32) GUINT32_FROM_LE (rsp->roam_pref);
+ if (!roam_pref_validate (roam_pref)) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "Unknown roam preference 0x%X",
+ roam_pref);
+ return NULL;
+ }
+
+ result = qcdm_result_new ();
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->call_state);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->oper_mode);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->system_mode);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->mode_pref);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->band_pref);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF, tmp_num);
+
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF, roam_pref);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->srv_domain_pref);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->acq_order_pref);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->hybrid_pref);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, tmp_num);
+
+ tmp_num = (guint32) GUINT32_FROM_LE (rsp->network_sel_mode_pref);
+ qcdm_result_add_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF, tmp_num);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_hdr_subsys_state_info_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[sizeof (DMCmdSubsysHeader) + 2];
+ DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_SUBSYS;
+ cmd->subsys_id = DIAG_SUBSYS_HDR;
+ cmd->subsys_cmd = GUINT16_TO_LE (DIAG_SUBSYS_HDR_STATE_INFO);
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_hdr_subsys_state_info_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdSubsysHDRStateInfoRsp *rsp = (DMCmdSubsysHDRStateInfoRsp *) buf;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysHDRStateInfoRsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE, rsp->at_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, rsp->session_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, rsp->almp_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE, rsp->init_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE, rsp->idle_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE, rsp->connected_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE_STATE, rsp->route_update_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG_STATE, rsp->overhead_msg_state);
+ qcdm_result_add_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE, rsp->hdr_hybrid_mode);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_zte_subsys_status_new (char *buf, gsize len, GError **error)
+{
+ char cmdbuf[sizeof (DMCmdSubsysHeader) + 2];
+ DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->code = DIAG_CMD_SUBSYS;
+ cmd->subsys_id = DIAG_SUBSYS_ZTE;
+ cmd->subsys_cmd = GUINT16_TO_LE (DIAG_SUBSYS_ZTE_STATUS);
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_zte_subsys_status_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdSubsysZteStatusRsp *rsp = (DMCmdSubsysZteStatusRsp *) buf;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysZteStatusRsp), error))
+ return NULL;
+
+ result = qcdm_result_new ();
+
+ qcdm_result_add_uint8 (result, QCDM_CMD_ZTE_SUBSYS_STATUS_ITEM_SIGNAL_INDICATOR, rsp->signal_ind);
+
+ return result;
+}
+
+/**********************************************************************/
+
+gsize
+qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (char *buf,
+ gsize len,
+ guint8 chipset,
+ GError **error)
+{
+ char cmdbuf[sizeof (DMCmdSubsysNwSnapshotReq) + 2];
+ DMCmdSubsysNwSnapshotReq *cmd = (DMCmdSubsysNwSnapshotReq *) &cmdbuf[0];
+
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
+
+ /* Validate chipset */
+ if (chipset != QCDM_NW_CHIPSET_6500 && chipset != QCDM_NW_CHIPSET_6800) {
+ g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER,
+ "Unknown Novatel chipset 0x%X",
+ chipset);
+ return 0;
+ }
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->hdr.code = DIAG_CMD_SUBSYS;
+ switch (chipset) {
+ case QCDM_NW_CHIPSET_6500:
+ cmd->hdr.subsys_id = DIAG_SUBSYS_NW_CONTROL_6500;
+ break;
+ case QCDM_NW_CHIPSET_6800:
+ cmd->hdr.subsys_id = DIAG_SUBSYS_NW_CONTROL_6800;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ cmd->hdr.subsys_cmd = GUINT16_TO_LE (DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT);
+ cmd->technology = DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT_TECH_CDMA_EVDO;
+ cmd->snapshot_mask = GUINT32_TO_LE (0xFFFF);
+
+ return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
+}
+
+QCDMResult *
+qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf, gsize len, GError **error)
+{
+ QCDMResult *result = NULL;
+ DMCmdSubsysNwSnapshotRsp *rsp = (DMCmdSubsysNwSnapshotRsp *) buf;
+ DMCmdSubsysNwSnapshotCdma *cdma = (DMCmdSubsysNwSnapshotCdma *) &rsp->data;
+ guint32 num;
+ guint8 num8;
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysNwSnapshotRsp), error))
+ return NULL;
+
+ /* FIXME: check response_code when we know what it means */
+
+ result = qcdm_result_new ();
+
+ num = GUINT32_FROM_LE (cdma->rssi);
+ qcdm_result_add_uint32 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_RSSI, num);
+
+ num8 = cdma_prev_to_qcdm (cdma->prev);
+ qcdm_result_add_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_PREV, num8);
+
+ num8 = cdma_band_class_to_qcdm (cdma->band_class);
+ qcdm_result_add_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_BAND_CLASS, num8);
+
+ qcdm_result_add_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI, cdma->eri);
+
+ num8 = QCDM_HDR_REV_UNKNOWN;
+ switch (cdma->hdr_rev) {
+ case 0:
+ num8 = QCDM_HDR_REV_0;
+ break;
+ case 1:
+ num8 = QCDM_HDR_REV_A;
+ break;
+ default:
+ break;
+ }
+ qcdm_result_add_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, num8);
+
+ return result;
+}
+
+/**********************************************************************/
+
diff --git a/libqcdm/src/commands.h b/libqcdm/src/commands.h
new file mode 100644
index 0000000..75e83a7
--- /dev/null
+++ b/libqcdm/src/commands.h
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_COMMANDS_H
+#define LIBQCDM_COMMANDS_H
+
+#include <glib.h>
+
+#include "result.h"
+
+/**********************************************************************/
+
+/* Generic enums */
+
+enum {
+ QCDM_CDMA_PREV_UNKNOWN = 0,
+ QCDM_CDMA_PREV_IS_95 = 1, /* and J_STD008 */
+ QCDM_CDMA_PREV_IS_95A = 2,
+ QCDM_CDMA_PREV_IS_95A_TSB74 = 3,
+ QCDM_CDMA_PREV_IS_95B_PHASE1 = 4,
+ QCDM_CDMA_PREV_IS_95B_PHASE2 = 5,
+ QCDM_CDMA_PREV_IS2000_REL0 = 6,
+ QCDM_CDMA_PREV_IS2000_RELA = 7
+};
+
+enum {
+ QCDM_CDMA_BAND_CLASS_UNKNOWN = 0,
+ QCDM_CDMA_BAND_CLASS_0_CELLULAR_800 = 1, /* US cellular 850MHz */
+ QCDM_CDMA_BAND_CLASS_1_PCS = 2, /* US PCS 1900MHz */
+ QCDM_CDMA_BAND_CLASS_2_TACS = 3,
+ QCDM_CDMA_BAND_CLASS_3_JTACS = 4, /* Japanese TACS */
+ QCDM_CDMA_BAND_CLASS_4_KOREAN_PCS = 5,
+ QCDM_CDMA_BAND_CLASS_5_NMT450 = 6,
+ QCDM_CDMA_BAND_CLASS_6_IMT2000 = 7, /* 2100HMz */
+ QCDM_CDMA_BAND_CLASS_7_CELLULAR_700 = 8,
+ QCDM_CDMA_BAND_CLASS_8_1800 = 9,
+ QCDM_CDMA_BAND_CLASS_9_900 = 10,
+ QCDM_CDMA_BAND_CLASS_10_SECONDARY_800 = 11,
+ QCDM_CDMA_BAND_CLASS_11_PAMR_400 = 12,
+ QCDM_CDMA_BAND_CLASS_12_PAMR_800 = 13
+};
+
+enum {
+ QCDM_HDR_REV_UNKNOWN = 0x00,
+ QCDM_HDR_REV_0 = 0x01,
+ QCDM_HDR_REV_A = 0x02
+};
+
+/**********************************************************************/
+
+#define QCDM_CMD_VERSION_INFO_ITEM_COMP_DATE "comp-date"
+#define QCDM_CMD_VERSION_INFO_ITEM_COMP_TIME "comp-time"
+#define QCDM_CMD_VERSION_INFO_ITEM_RELEASE_DATE "release-date"
+#define QCDM_CMD_VERSION_INFO_ITEM_RELEASE_TIME "release-time"
+#define QCDM_CMD_VERSION_INFO_ITEM_MODEL "model"
+
+gsize qcdm_cmd_version_info_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_version_info_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+#define QCDM_CMD_ESN_ITEM_ESN "esn"
+
+gsize qcdm_cmd_esn_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_esn_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+/* Values for QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE */
+enum {
+ QCDM_CMD_CDMA_STATUS_RF_MODE_ANALOG = 0,
+ QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_CELLULAR = 1,
+ QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_PCS = 2,
+ QCDM_CMD_CDMA_STATUS_RF_MODE_SLEEP = 3,
+ QCDM_CMD_CDMA_STATUS_RF_MODE_GPS = 4,
+ QCDM_CMD_CDMA_STATUS_RF_MODE_HDR = 5,
+};
+
+/* Values for QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE */
+enum {
+ QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA = 0,
+ QCDM_CMD_CDMA_STATUS_RX_STATE_SYNC_CHANNEL = 1,
+ QCDM_CMD_CDMA_STATUS_RX_STATE_PAGING_CHANNEL = 2,
+ QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL_INIT = 3,
+ QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL = 4,
+ QCDM_CMD_CDMA_STATUS_RX_STATE_EXITING_CDMA = 5,
+};
+
+#define QCDM_CMD_CDMA_STATUS_ITEM_ESN "esn"
+#define QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE "rf-mode"
+#define QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE "rx-state"
+#define QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON "entry-reason"
+#define QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL "current-channel"
+#define QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL "code-channel"
+#define QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE "pilot-base"
+#define QCDM_CMD_CDMA_STATUS_ITEM_SID "sid"
+#define QCDM_CMD_CDMA_STATUS_ITEM_NID "nid"
+
+gsize qcdm_cmd_cdma_status_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_cdma_status_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+/* NOTE: this command does not appear to be implemented in recent
+ * devices and probably returns (QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND).
+ */
+
+#define QCDM_CMD_SW_VERSION_ITEM_VERSION "version"
+#define QCDM_CMD_SW_VERSION_ITEM_COMP_DATE "comp-date"
+#define QCDM_CMD_SW_VERSION_ITEM_COMP_TIME "comp-time"
+
+gsize qcdm_cmd_sw_version_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_sw_version_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+enum {
+ QCDM_CMD_PILOT_SETS_TYPE_UNKNOWN = 0,
+ QCDM_CMD_PILOT_SETS_TYPE_ACTIVE = 1,
+ QCDM_CMD_PILOT_SETS_TYPE_CANDIDATE = 2,
+ QCDM_CMD_PILOT_SETS_TYPE_NEIGHBOR = 3,
+};
+
+gsize qcdm_cmd_pilot_sets_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_pilot_sets_result (const char *buf,
+ gsize len,
+ GError **error);
+
+gboolean qcdm_cmd_pilot_sets_result_get_num (QCDMResult *result,
+ guint32 set_type,
+ guint32 *out_num);
+
+gboolean qcdm_cmd_pilot_sets_result_get_pilot (QCDMResult *result,
+ guint32 set_type,
+ guint32 num,
+ guint32 *out_pn_offset,
+ guint32 *out_ecio,
+ float *out_db);
+
+/**********************************************************************/
+
+#define QCDM_CMD_NV_GET_MDN_ITEM_PROFILE "profile"
+#define QCDM_CMD_NV_GET_MDN_ITEM_MDN "mdn"
+
+gsize qcdm_cmd_nv_get_mdn_new (char *buf,
+ gsize len,
+ guint8 profile,
+ GError **error);
+
+QCDMResult *qcdm_cmd_nv_get_mdn_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+/* Values for QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF */
+enum {
+ QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_HOME_ONLY = 0x01,
+ QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_ROAM_ONLY = 0x06,
+ QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_AUTO = 0xFF,
+};
+
+#define QCDM_CMD_NV_GET_ROAM_PREF_ITEM_PROFILE "profile"
+#define QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF "roam-pref"
+
+gsize qcdm_cmd_nv_get_roam_pref_new (char *buf,
+ gsize len,
+ guint8 profile,
+ GError **error);
+
+QCDMResult *qcdm_cmd_nv_get_roam_pref_result (const char *buf,
+ gsize len,
+ GError **error);
+
+gsize qcdm_cmd_nv_set_roam_pref_new (char *buf,
+ gsize len,
+ guint8 profile,
+ guint8 roam_pref,
+ GError **error);
+
+QCDMResult *qcdm_cmd_nv_set_roam_pref_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+/* Values for QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF */
+enum {
+ QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO = 0x04,
+ QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY = 0x09,
+ QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY = 0x0A,
+};
+
+#define QCDM_CMD_NV_GET_MODE_PREF_ITEM_PROFILE "profile"
+#define QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF "mode-pref"
+
+gsize qcdm_cmd_nv_get_mode_pref_new (char *buf,
+ gsize len,
+ guint8 profile,
+ GError **error);
+
+QCDMResult *qcdm_cmd_nv_get_mode_pref_result (const char *buf,
+ gsize len,
+ GError **error);
+
+gsize qcdm_cmd_nv_set_mode_pref_new (char *buf,
+ gsize len,
+ guint8 profile,
+ guint8 mode_pref,
+ GError **error);
+
+QCDMResult *qcdm_cmd_nv_set_mode_pref_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+/* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE */
+enum {
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE = 5
+};
+
+/* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE */
+enum {
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE = 0,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS = 1,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA = 2,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GSM = 3,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR = 4,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA = 5,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GPS = 6
+};
+
+/* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF */
+enum {
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_HOME_ONLY = 0x01,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_ROAM_ONLY = 0x06,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_AUTO = 0xFF,
+};
+
+/* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF */
+enum {
+ /* Note: not the same values as QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF has;
+ * AUTO really is 0x02 here, not 0x04 like the NV item value for AUTO.
+ */
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_DIGITAL_ONLY = 0x01,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_AUTO = 0x02,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_1X_ONLY = 0x09,
+ QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_HDR_ONLY = 0x0A,
+};
+
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE "call-state"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE "operating-mode"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE "system-mode"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF "mode-pref"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF "band-pref"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF "roam-pref"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF "service-domain-pref"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF "acq-order-pref"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF "hybrid-pref"
+#define QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF "network-selection-pref"
+
+gsize qcdm_cmd_cm_subsys_state_info_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_cm_subsys_state_info_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_INACTIVE = 0,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACQUISITION = 1,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_SYNC = 2,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_IDLE = 3,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACCESS = 4,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_CONNECTED = 5
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED = 0,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_SETUP = 1,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AT_INIT = 2, /* initiated by Access Terminal */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AN_INIT = 3, /* initiated by Access Node */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN = 4,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSING = 5
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE (TIA-856-A section 9.2.1) */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE = 0, /* initial state */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INIT = 1, /* terminal has yet to acquire network */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE = 2, /* network acquired but no connection */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED = 3, /* open connection to the network */
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE (TIA-856-A section 9.3.1) */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_INACTIVE = 0, /* protocol waiting for ACTIVATE command */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_NET_DETERMINE = 1, /* choosing a network to operate on */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_ACQUISITION = 2, /* acquiring Forward Pilot Channel */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_SYNC = 3, /* synchronizing to Control Channel */
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE (TIA-856-A section 9.4.1) */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_INACTIVE = 0, /* protocol waiting for ACTIVATE command */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SLEEP = 1, /* sleeping */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_MONITOR = 2, /* monitoring the Control Channel */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SETUP = 3, /* setting up a connection */
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE (TIA-856-A section 9.6.1) */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_INACTIVE = 0, /* protocol waiting for ACTIVATE command */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_OPEN = 1, /* connection is open */
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_CLOSING = 2, /* connection is closed */
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE (TIA-856-A section 9.7.1) */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_INACTIVE = 0,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_IDLE = 1,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_CONNECTED = 2,
+};
+
+/* Values for QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG (TIA-856-A section 9.9.1) */
+enum {
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INIT = 0,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INACTIVE = 1,
+ QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_ACTIVE = 2,
+};
+
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE "at-state" /* State of Access Terminal */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE "session-state" /* Current session state */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE "almp-state" /* Air Link Management Protocol (ALMP) state */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE "init-state" /* Initialization State Protocol (ISP) state */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE "idle-state" /* Idle State Protocol (IDP) state */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE "connected-state" /* Connected State Protocol (CSP) state */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE_STATE "route-update-state" /* Route Update Protocol (RUP) state */
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG_STATE "overhead-msg-state"
+#define QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE "hdr-hybrid-mode"
+
+gsize qcdm_cmd_hdr_subsys_state_info_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_hdr_subsys_state_info_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+#define QCDM_CMD_ZTE_SUBSYS_STATUS_ITEM_SIGNAL_INDICATOR "signal-indicator"
+
+gsize qcdm_cmd_zte_subsys_status_new (char *buf,
+ gsize len,
+ GError **error);
+
+QCDMResult *qcdm_cmd_zte_subsys_status_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+#define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_RSSI "rssi"
+
+/* One of QCDM_CDMA_PREV_* */
+#define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_PREV "prev"
+
+/* One of QCDM_CDMA_BAND_CLASS_* */
+#define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_BAND_CLASS "band-class"
+
+#define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI "eri"
+
+/* One of QCDM_HDR_REV_* */
+#define QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV "hdr-rev"
+
+enum {
+ QCDM_NW_CHIPSET_UNKNOWN = 0,
+ QCDM_NW_CHIPSET_6500 = 1,
+ QCDM_NW_CHIPSET_6800 = 2,
+};
+
+gsize qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (char *buf,
+ gsize len,
+ guint8 chipset,
+ GError **error);
+
+QCDMResult *qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf,
+ gsize len,
+ GError **error);
+
+/**********************************************************************/
+
+#endif /* LIBQCDM_COMMANDS_H */
diff --git a/libqcdm/src/dm-commands.h b/libqcdm/src/dm-commands.h
new file mode 100644
index 0000000..cfc98f7
--- /dev/null
+++ b/libqcdm/src/dm-commands.h
@@ -0,0 +1,370 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_DM_COMMANDS_H
+#define LIBQCDM_DM_COMMANDS_H
+
+enum {
+ DIAG_CMD_VERSION_INFO = 0, /* Version info */
+ DIAG_CMD_ESN = 1, /* ESN */
+ DIAG_CMD_PEEKB = 2, /* Peek byte */
+ DIAG_CMD_PEEKW = 3, /* Peek word */
+ DIAG_CMD_PEEKD = 4, /* Peek dword */
+ DIAG_CMD_POKEB = 5, /* Poke byte */
+ DIAG_CMD_POKEW = 6, /* Poke word */
+ DIAG_CMD_POKED = 7, /* Poke dword */
+ DIAG_CMD_OUTP = 8, /* Byte output */
+ DIAG_CMD_OUTPW = 9, /* Word output */
+ DIAG_CMD_INP = 10, /* Byte input */
+ DIAG_CMD_INPW = 11, /* Word input */
+ DIAG_CMD_STATUS = 12, /* Station status */
+ DIAG_CMD_LOGMASK = 15, /* Set logging mask */
+ DIAG_CMD_LOG = 16, /* Log packet */
+ DIAG_CMD_NV_PEEK = 17, /* Peek NV memory */
+ DIAG_CMD_NV_POKE = 18, /* Poke NV memory */
+ DIAG_CMD_BAD_CMD = 19, /* Invalid command (response) */
+ DIAG_CMD_BAD_PARM = 20, /* Invalid parameter (response) */
+ DIAG_CMD_BAD_LEN = 21, /* Invalid packet length (response) */
+ DIAG_CMD_BAD_DEV = 22, /* Not accepted by the device (response) */
+ DIAG_CMD_BAD_MODE = 24, /* Not allowed in this mode (response) */
+ DIAG_CMD_TAGRAPH = 25, /* Info for TA power and voice graphs */
+ DIAG_CMD_MARKOV = 26, /* Markov stats */
+ DIAG_CMD_MARKOV_RESET = 27, /* Reset Markov stats */
+ DIAG_CMD_DIAG_VER = 28, /* Diagnostic Monitor version */
+ DIAG_CMD_TIMESTAMP = 29, /* Return a timestamp */
+ DIAG_CMD_TA_PARM = 30, /* Set TA parameters */
+ DIAG_CMD_MESSAGE = 31, /* Request for msg report */
+ DIAG_CMD_HS_KEY = 32, /* Handset emulation -- keypress */
+ DIAG_CMD_HS_LOCK = 33, /* Handset emulation -- lock or unlock */
+ DIAG_CMD_HS_SCREEN = 34, /* Handset emulation -- display request */
+ DIAG_CMD_PARM_SET = 36, /* Parameter download */
+ DIAG_CMD_NV_READ = 38, /* Read NV item */
+ DIAG_CMD_NV_WRITE = 39, /* Write NV item */
+ DIAG_CMD_CONTROL = 41, /* Mode change request */
+ DIAG_CMD_ERR_READ = 42, /* Error record retreival */
+ DIAG_CMD_ERR_CLEAR = 43, /* Error record clear */
+ DIAG_CMD_SER_RESET = 44, /* Symbol error rate counter reset */
+ DIAG_CMD_SER_REPORT = 45, /* Symbol error rate counter report */
+ DIAG_CMD_TEST = 46, /* Run a specified test */
+ DIAG_CMD_GET_DIPSW = 47, /* Retreive the current DIP switch setting */
+ DIAG_CMD_SET_DIPSW = 48, /* Write new DIP switch setting */
+ DIAG_CMD_VOC_PCM_LB = 49, /* Start/Stop Vocoder PCM loopback */
+ DIAG_CMD_VOC_PKT_LB = 50, /* Start/Stop Vocoder PKT loopback */
+ DIAG_CMD_ORIG = 53, /* Originate a call */
+ DIAG_CMD_END = 54, /* End a call */
+ DIAG_CMD_SW_VERSION = 56, /* Get software version */
+ DIAG_CMD_DLOAD = 58, /* Switch to downloader */
+ DIAG_CMD_TMOB = 59, /* Test Mode Commands and FTM commands*/
+ DIAG_CMD_STATE = 63, /* Current state of the phone */
+ DIAG_CMD_PILOT_SETS = 64, /* Return all current sets of pilots */
+ DIAG_CMD_SPC = 65, /* Send the Service Programming Code to unlock */
+ DIAG_CMD_BAD_SPC_MODE = 66, /* Invalid NV read/write because SP is locked */
+ DIAG_CMD_PARM_GET2 = 67, /* (obsolete) */
+ DIAG_CMD_SERIAL_CHG = 68, /* Serial mode change */
+ DIAG_CMD_PASSWORD = 70, /* Send password to unlock secure operations */
+ DIAG_CMD_BAD_SEC_MODE = 71, /* Operation not allowed in this security state */
+ DIAG_CMD_PRL_WRITE = 72, /* Write PRL */
+ DIAG_CMD_PRL_READ = 73, /* Read PRL */
+ DIAG_CMD_SUBSYS = 75, /* Subsystem commands */
+ DIAG_CMD_FEATURE_QUERY = 81,
+ DIAG_CMD_SMS_READ = 83, /* Read SMS message out of NV memory */
+ DIAG_CMD_SMS_WRITE = 84, /* Write SMS message into NV memory */
+ DIAG_CMD_SUP_FER = 85, /* Frame Error Rate info on multiple channels */
+ DIAG_CMD_SUP_WALSH_CODES = 86, /* Supplemental channel walsh codes */
+ DIAG_CMD_SET_MAX_SUP_CH = 87, /* Sets the maximum # supplemental channels */
+ DIAG_CMD_PARM_GET_IS95B = 88, /* Get parameters including SUPP and MUX2 */
+ DIAG_CMD_FS_OP = 89, /* Embedded File System (EFS) operations */
+ DIAG_CMD_AKEY_VERIFY = 90, /* AKEY Verification */
+ DIAG_CMD_HS_BMP_SCREEN = 91, /* Handset Emulation -- Bitmap screen */
+ DIAG_CMD_CONFIG_COMM = 92, /* Configure communications */
+ DIAG_CMD_EXT_LOGMASK = 93, /* Extended logmask for > 32 bits */
+ DIAG_CMD_EVENT_REPORT = 96, /* Static Event reporting */
+ DIAG_CMD_STREAMING_CONFIG = 97, /* Load balancing etc */
+ DIAG_CMD_PARM_RETRIEVE = 98, /* Parameter retrieval */
+ DIAG_CMD_STATUS_SNAPSHOT = 99, /* Status snapshot */
+ DIAG_CMD_RPC = 100, /* Used for RPC */
+ DIAG_CMD_GET_PROPERTY = 101,
+ DIAG_CMD_PUT_PROPERTY = 102,
+ DIAG_CMD_GET_GUID = 103, /* GUID requests */
+ DIAG_CMD_USER_CMD = 104, /* User callbacks */
+ DIAG_CMD_GET_PERM_PROPERTY = 105,
+ DIAG_CMD_PUT_PERM_PROPERTY = 106,
+ DIAG_CMD_PERM_USER_CMD = 107, /* Permanent user callbacks */
+ DIAG_CMD_GPS_SESS_CTRL = 108, /* GPS session control */
+ DIAG_CMD_GPS_GRID = 109, /* GPS search grid */
+ DIAG_CMD_GPS_STATISTICS = 110,
+ DIAG_CMD_TUNNEL = 111, /* Tunneling command code */
+ DIAG_CMD_RAM_RW = 112, /* Calibration RAM control using DM */
+ DIAG_CMD_CPU_RW = 113, /* Calibration CPU control using DM */
+ DIAG_CMD_SET_FTM_TEST_MODE = 114, /* Field (or Factory?) Test Mode */
+};
+
+/* Subsystem IDs used with DIAG_CMD_SUBSYS; these often obsolete many of
+ * the original DM commands.
+ */
+enum {
+ DIAG_SUBSYS_HDR = 5, /* High Data Rate (ie, EVDO) */
+ DIAG_SUBSYS_GPS = 13,
+ DIAG_SUBSYS_SMS = 14,
+ DIAG_SUBSYS_CM = 15, /* Call manager */
+ DIAG_SUBSYS_NW_CONTROL_6500 = 50, /* for Novatel Wireless MSM6500-based devices */
+ DIAG_SUBSYS_ZTE = 101, /* for ZTE EVDO devices */
+ DIAG_SUBSYS_NW_CONTROL_6800 = 250 /* for Novatel Wireless MSM6800-based devices */
+};
+
+/* HDR subsystem command codes */
+enum {
+ DIAG_SUBSYS_HDR_STATE_INFO = 8, /* Gets EVDO state */
+};
+
+enum {
+ DIAG_SUBSYS_CM_STATE_INFO = 0, /* Gets Call Manager state */
+};
+
+/* NW_CONTROL subsystem command codes (only for Novatel Wireless devices) */
+enum {
+ DIAG_SUBSYS_NW_CONTROL_AT_REQUEST = 3, /* AT commands via diag */
+ DIAG_SUBSYS_NW_CONTROL_AT_RESPONSE = 4,
+ DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT = 7,
+ DIAG_SUBSYS_NW_CONTROL_ERI = 8, /* Extended Roaming Indicator */
+ DIAG_SUBSYS_NW_CONTROL_PRL = 12,
+};
+
+enum {
+ DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT_TECH_CDMA_EVDO = 7,
+ DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT_TECH_WCDMA = 20,
+};
+
+enum {
+ DIAG_SUBSYS_ZTE_STATUS = 0,
+};
+
+enum {
+ CDMA_PREV_UNKNOWN = 0,
+ CDMA_PREV_IS_95 = 1, /* and J_STD008 */
+ CDMA_PREV_IS_95A = 2,
+ CDMA_PREV_IS_95A_TSB74 = 3,
+ CDMA_PREV_IS_95B_PHASE1 = 4,
+ CDMA_PREV_IS_95B_PHASE2 = 5,
+ CDMA_PREV_IS2000_REL0 = 6,
+ CDMA_PREV_IS2000_RELA = 7
+};
+
+enum {
+ CDMA_BAND_CLASS_0_CELLULAR_800 = 0, /* US cellular 850MHz */
+ CDMA_BAND_CLASS_1_PCS = 1, /* US PCS 1900MHz */
+ CDMA_BAND_CLASS_2_TACS = 2,
+ CDMA_BAND_CLASS_3_JTACS = 3, /* Japanese TACS */
+ CDMA_BAND_CLASS_4_KOREAN_PCS = 4,
+ CDMA_BAND_CLASS_5_NMT450 = 5,
+ CDMA_BAND_CLASS_6_IMT2000 = 6, /* 2100HMz */
+ CDMA_BAND_CLASS_7_CELLULAR_700 = 7,
+ CDMA_BAND_CLASS_8_1800 = 8,
+ CDMA_BAND_CLASS_9_900 = 9,
+ CDMA_BAND_CLASS_10_SECONDARY_800 = 10,
+ CDMA_BAND_CLASS_11_PAMR_400 = 11,
+ CDMA_BAND_CLASS_12_PAMR_800 = 12
+};
+
+/* Generic DM command header */
+struct DMCmdHeader {
+ guint8 code;
+} __attribute__ ((packed));
+typedef struct DMCmdHeader DMCmdHeader;
+
+/* DIAG_CMD_SUBSYS */
+struct DMCmdSubsysHeader {
+ guint8 code;
+ guint8 subsys_id;
+ guint16 subsys_cmd;
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysHeader DMCmdSubsysHeader;
+
+/* DIAG_CMD_NV_READ / DIAG_CMD_NV_WRITE */
+struct DMCmdNVReadWrite {
+ guint8 code;
+ guint16 nv_item;
+ guint8 data[128];
+ guint16 status;
+} __attribute__ ((packed));
+typedef struct DMCmdNVReadWrite DMCmdNVReadWrite;
+
+/* DIAG_CMD_VERSION_INFO */
+struct DMCmdVersionInfoRsp {
+ guint8 code;
+ char comp_date[11];
+ char comp_time[8];
+ char rel_date[11];
+ char rel_time[8];
+ char model[8];
+ guint8 scm;
+ guint8 mob_cai_rev;
+ guint8 mob_model;
+ guint16 mob_firmware_rev;
+ guint8 slot_cycle_index;
+ guint8 msm_ver;
+ guint8 _unknown;
+} __attribute__ ((packed));
+typedef struct DMCmdVersionInfoRsp DMCmdVersionInfoRsp;
+
+/* DIAG_CMD_ESN */
+struct DMCmdEsnRsp {
+ guint8 code;
+ guint8 esn[4];
+} __attribute__ ((packed));
+typedef struct DMCmdEsnRsp DMCmdEsnRsp;
+
+/* DIAG_CMD_STATUS */
+struct DMCmdStatusRsp {
+ guint8 code;
+ guint8 _unknown[3];
+ guint8 esn[4];
+ guint16 rf_mode;
+ guint8 min1_analog[4];
+ guint8 min1_cdma[4];
+ guint8 min2_analog[2];
+ guint8 min2_cdma[2];
+ guint8 _unknown1;
+ guint16 cdma_rx_state;
+ guint8 good_frames;
+ guint16 analog_corrected_frames;
+ guint16 analog_bad_frames;
+ guint16 analog_word_syncs;
+ guint16 entry_reason;
+ guint16 curr_chan;
+ guint8 cdma_code_chan;
+ guint16 pilot_base;
+ guint16 sid;
+ guint16 nid;
+ guint16 analog_locaid;
+ guint16 analog_rssi;
+ guint8 analog_power;
+} __attribute__ ((packed));
+typedef struct DMCmdStatusRsp DMCmdStatusRsp;
+
+/* DIAG_CMD_SW_VERSION */
+struct DMCmdSwVersionRsp {
+ guint8 code;
+ char version[20];
+ char comp_date[11];
+ char comp_time[8];
+} __attribute__ ((packed));
+typedef struct DMCmdSwVersionRsp DMCmdSwVersionRsp;
+
+/* DIAG_SUBSYS_CM_STATE_INFO subsys command */
+struct DMCmdSubsysCMStateInfoRsp {
+ DMCmdSubsysHeader header;
+ guint32 call_state;
+ guint32 oper_mode;
+ guint32 system_mode;
+ guint32 mode_pref;
+ guint32 band_pref;
+ guint32 roam_pref;
+ guint32 srv_domain_pref;
+ guint32 acq_order_pref;
+ guint32 hybrid_pref;
+ guint32 network_sel_mode_pref;
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysCMStateInfoRsp DMCmdSubsysCMStateInfoRsp;
+
+/* DIAG_SUBSYS_HDR_STATE_INFO subsys command */
+struct DMCmdSubsysHDRStateInfoRsp {
+ DMCmdSubsysHeader header;
+ guint8 at_state;
+ guint8 session_state;
+ guint8 almp_state;
+ guint8 init_state;
+ guint8 idle_state;
+ guint8 connected_state;
+ guint8 route_update_state;
+ guint8 overhead_msg_state;
+ guint8 hdr_hybrid_mode;
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysHDRStateInfoRsp DMCmdSubsysHDRStateInfoRsp;
+
+
+/* DIAG_SUBSYS_ZTE_STATUS subsys command */
+struct DMCmdSubsysZteStatusRsp {
+ DMCmdSubsysHeader header;
+ guint8 _unknown1[8];
+ guint8 signal_ind;
+ guint8 _unknown2;
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysZteStatusRsp DMCmdSubsysZteStatusRsp;
+
+/* DIAG_CMD_PILOT_SETS command */
+struct DMCmdPilotSetsSet {
+ guint16 pn_offset;
+ guint16 ecio;
+} __attribute__ ((packed));
+typedef struct DMCmdPilotSetsSet DMCmdPilotSetsSet;
+
+struct DMCmdPilotSetsRsp {
+ guint8 code;
+ guint16 pilot_inc;
+ guint8 active_count;
+ guint8 candidate_count;
+ guint8 neighbor_count;
+ DMCmdPilotSetsSet sets[52];
+} __attribute__ ((packed));
+typedef struct DMCmdPilotSetsRsp DMCmdPilotSetsRsp;
+
+/* DIAG_SUBSYS_NW_CONTROL_* subsys command */
+struct DMCmdSubsysNwSnapshotReq {
+ DMCmdSubsysHeader hdr;
+ guint8 technology; /* DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT_TECH_* */
+ guint32 snapshot_mask;
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysNwSnapshotReq DMCmdSubsysNwSnapshotReq;
+
+/* DIAG_SUBSYS_NW_CONTROL_MODEM_SNAPSHOT response */
+struct DMCmdSubsysNwSnapshotRsp {
+ DMCmdSubsysHeader hdr;
+ guint8 response_code;
+ guint32 bitfield1;
+ guint32 bitfield2;
+ guint8 data[100];
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysNwSnapshotRsp DMCmdSubsysNwSnapshotRsp;
+
+struct DMCmdSubsysNwSnapshotCdma {
+ guint32 rssi;
+ guint32 battery_level;
+ guint8 call_info;
+ guint8 new_sms_ind;
+ guint8 missed_calls;
+ guint32 voicemail_ind;
+ guint8 pkt_call_ctrl_state;
+ guint8 mip_rrp_err_code;
+ guint8 cur_packet_zone_id;
+ guint8 prev;
+ guint8 band_class;
+ guint8 eri;
+ guint8 eri_alert_id;
+ guint32 cur_call_total_time;
+ guint32 cur_call_active_time;
+ guint32 cur_call_tx_ip_bytes;
+ guint32 cur_call_rx_ip_bytes;
+ guint8 connection_status;
+ guint16 dominant_pn;
+ guint8 wdisable_mask;
+ guint8 hdr_rev;
+} __attribute__ ((packed));
+typedef struct DMCmdSubsysNwSnapshotCdma DMCmdSubsysNwSnapshotCdma;
+
+#endif /* LIBQCDM_DM_COMMANDS_H */
+
diff --git a/libqcdm/src/error.c b/libqcdm/src/error.c
new file mode 100644
index 0000000..e3b97a0
--- /dev/null
+++ b/libqcdm/src/error.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "error.h"
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GQuark
+qcdm_serial_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (ret == 0)
+ ret = g_quark_from_static_string ("qcdm-serial-error");
+
+ return ret;
+}
+
+GType
+qcdm_serial_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (QCDM_SERIAL_CONFIG_FAILED, "SerialConfigFailed"),
+ { 0, 0, 0 }
+ };
+
+ etype = g_enum_register_static ("QcdmSerialError", values);
+ }
+
+ return etype;
+}
+
+/***************************************************************/
+
+GQuark
+qcdm_command_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (ret == 0)
+ ret = g_quark_from_static_string ("qcdm-command-error");
+
+ return ret;
+}
+
+GType
+qcdm_command_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (QCDM_COMMAND_MALFORMED_RESPONSE, "QcdmCommandMalformedResponse"),
+ ENUM_ENTRY (QCDM_COMMAND_UNEXPECTED, "QcdmCommandUnexpected"),
+ ENUM_ENTRY (QCDM_COMMAND_BAD_LENGTH, "QcdmCommandBadLength"),
+ ENUM_ENTRY (QCDM_COMMAND_BAD_COMMAND, "QcdmCommandBadCommand"),
+ ENUM_ENTRY (QCDM_COMMAND_BAD_PARAMETER, "QcdmCommandBadParameter"),
+ ENUM_ENTRY (QCDM_COMMAND_NOT_ACCEPTED, "QcdmCommandNotAccepted"),
+ ENUM_ENTRY (QCDM_COMMAND_BAD_MODE, "QcdmCommandBadMode"),
+ ENUM_ENTRY (QCDM_COMMAND_NVCMD_FAILED, "QcdmCommandNvCmdFailed"),
+ { 0, 0, 0 }
+ };
+
+ etype = g_enum_register_static ("QcdmCommandError", values);
+ }
+
+ return etype;
+}
+
+
diff --git a/libqcdm/src/error.h b/libqcdm/src/error.h
new file mode 100644
index 0000000..7a02ae2
--- /dev/null
+++ b/libqcdm/src/error.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_ERROR_H
+#define LIBQCDM_ERROR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+enum {
+ QCDM_SERIAL_CONFIG_FAILED = 0,
+};
+
+#define QCDM_SERIAL_ERROR (qcdm_serial_error_quark ())
+#define QCDM_TYPE_SERIAL_ERROR (qcdm_serial_error_get_type ())
+
+GQuark qcdm_serial_error_quark (void);
+GType qcdm_serial_error_get_type (void);
+
+
+enum {
+ QCDM_COMMAND_MALFORMED_RESPONSE = 0,
+ QCDM_COMMAND_UNEXPECTED = 1,
+ QCDM_COMMAND_BAD_LENGTH = 2,
+ QCDM_COMMAND_BAD_COMMAND = 3,
+ QCDM_COMMAND_BAD_PARAMETER = 4,
+ QCDM_COMMAND_NOT_ACCEPTED = 5,
+ QCDM_COMMAND_BAD_MODE = 6,
+ QCDM_COMMAND_NVCMD_FAILED = 7,
+};
+
+#define QCDM_COMMAND_ERROR (qcdm_command_error_quark ())
+#define QCDM_TYPE_COMMAND_ERROR (qcdm_command_error_get_type ())
+
+GQuark qcdm_command_error_quark (void);
+GType qcdm_command_error_get_type (void);
+
+#endif /* LIBQCDM_ERROR_H */
+
diff --git a/libqcdm/src/libqcdm.ver b/libqcdm/src/libqcdm.ver
new file mode 100644
index 0000000..b1567e2
--- /dev/null
+++ b/libqcdm/src/libqcdm.ver
@@ -0,0 +1,6 @@
+{
+global:
+ nm_vpn_connection_new;
+local:
+ *;
+};
diff --git a/libqcdm/src/nv-items.h b/libqcdm/src/nv-items.h
new file mode 100644
index 0000000..a0ca10a
--- /dev/null
+++ b/libqcdm/src/nv-items.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_NV_ITEMS_H
+#define LIBQCDM_NV_ITEMS_H
+
+enum {
+ DIAG_NV_MODE_PREF = 10, /* Mode preference: 1x, HDR, auto */
+ DIAG_NV_DIR_NUMBER = 178, /* Mobile Directory Number (MDN) */
+ DIAG_NV_ROAM_PREF = 442, /* Roaming preference */
+};
+
+
+/* Mode preference values */
+enum {
+ DIAG_NV_MODE_PREF_AUTO = 0x04,
+ DIAG_NV_MODE_PREF_1X_ONLY = 0x09,
+ DIAG_NV_MODE_PREF_HDR_ONLY = 0x0A,
+};
+
+/* DIAG_NV_MODE_PREF */
+struct DMNVItemModePref {
+ guint8 profile;
+ guint8 mode_pref;
+} __attribute__ ((packed));
+typedef struct DMNVItemModePref DMNVItemModePref;
+
+/* DIAG_NV_DIR_NUMBER */
+struct DMNVItemMdn {
+ guint8 profile;
+ guint8 mdn[10];
+} __attribute__ ((packed));
+typedef struct DMNVItemMdn DMNVItemMdn;
+
+/* Roam preference values */
+enum {
+ DIAG_NV_ROAM_PREF_HOME_ONLY = 0x01,
+ DIAG_NV_ROAM_PREF_ROAM_ONLY = 0x06,
+ DIAG_NV_ROAM_PREF_AUTO = 0xFF,
+};
+
+/* DIAG_NV_ROAM_PREF */
+struct DMNVItemRoamPref {
+ guint8 profile;
+ guint8 roam_pref;
+} __attribute__ ((packed));
+typedef struct DMNVItemRoamPref DMNVItemRoamPref;
+
+#endif /* LIBQCDM_NV_ITEMS_H */
+
diff --git a/libqcdm/src/result-private.h b/libqcdm/src/result-private.h
new file mode 100644
index 0000000..0db63f5
--- /dev/null
+++ b/libqcdm/src/result-private.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_RESULT_PRIVATE_H
+#define LIBQCDM_RESULT_PRIVATE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "result.h"
+
+QCDMResult *qcdm_result_new (void);
+
+/* For these functions, 'key' *must* be a constant, not allocated and freed */
+
+void qcdm_result_add_string (QCDMResult *result,
+ const char *key,
+ const char *str);
+
+void qcdm_result_add_uint8 (QCDMResult *result,
+ const char *key,
+ guint8 num);
+
+void qcdm_result_add_uint32 (QCDMResult *result,
+ const char *key,
+ guint32 num);
+
+void qcdm_result_add_boxed (QCDMResult *result,
+ const char *key,
+ GType btype,
+ gpointer boxed);
+
+gboolean qcdm_result_get_boxed (QCDMResult *result,
+ const char *key,
+ gpointer *out_val);
+
+#endif /* LIBQCDM_RESULT_PRIVATE_H */
+
diff --git a/libqcdm/src/result.c b/libqcdm/src/result.c
new file mode 100644
index 0000000..2440478
--- /dev/null
+++ b/libqcdm/src/result.c
@@ -0,0 +1,248 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "result.h"
+#include "result-private.h"
+#include "error.h"
+
+struct QCDMResult {
+ guint32 refcount;
+ GHashTable *hash;
+};
+
+
+static void
+gvalue_destroy (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+QCDMResult *
+qcdm_result_new (void)
+{
+ QCDMResult *result;
+
+ g_type_init ();
+
+ result = g_malloc0 (sizeof (QCDMResult));
+ result->hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, gvalue_destroy);
+ result->refcount = 1;
+ return result;
+}
+
+QCDMResult *
+qcdm_result_ref (QCDMResult *result)
+{
+ g_return_val_if_fail (result != NULL, NULL);
+ g_return_val_if_fail (result->refcount > 0, NULL);
+
+ result->refcount++;
+ return result;
+}
+
+void
+qcdm_result_unref (QCDMResult *result)
+{
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (result->refcount > 0);
+
+ result->refcount--;
+ if (result->refcount == 0) {
+ g_hash_table_destroy (result->hash);
+ memset (result, 0, sizeof (QCDMResult));
+ g_free (result);
+ }
+}
+
+void
+qcdm_result_add_string (QCDMResult *result,
+ const char *key,
+ const char *str)
+{
+ GValue *val;
+
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (result->refcount > 0);
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (str != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+
+ g_hash_table_insert (result->hash, (gpointer) key, val);
+}
+
+gboolean
+qcdm_result_get_string (QCDMResult *result,
+ const char *key,
+ const char **out_val)
+{
+ GValue *val;
+
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (result->refcount > 0, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (out_val != NULL, FALSE);
+ g_return_val_if_fail (*out_val == NULL, FALSE);
+
+ val = g_hash_table_lookup (result->hash, key);
+ if (!val)
+ return FALSE;
+
+ g_warn_if_fail (G_VALUE_HOLDS_STRING (val));
+ if (!G_VALUE_HOLDS_STRING (val))
+ return FALSE;
+
+ *out_val = g_value_get_string (val);
+ return TRUE;
+}
+
+void
+qcdm_result_add_uint8 (QCDMResult *result,
+ const char *key,
+ guint8 num)
+{
+ GValue *val;
+
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (result->refcount > 0);
+ g_return_if_fail (key != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UCHAR);
+ g_value_set_uchar (val, (unsigned char) num);
+
+ g_hash_table_insert (result->hash, (gpointer) key, val);
+}
+
+gboolean
+qcdm_result_get_uint8 (QCDMResult *result,
+ const char *key,
+ guint8 *out_val)
+{
+ GValue *val;
+
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (result->refcount > 0, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (out_val != NULL, FALSE);
+
+ val = g_hash_table_lookup (result->hash, key);
+ if (!val)
+ return FALSE;
+
+ g_warn_if_fail (G_VALUE_HOLDS_UCHAR (val));
+ if (!G_VALUE_HOLDS_UCHAR (val))
+ return FALSE;
+
+ *out_val = (guint8) g_value_get_uchar (val);
+ return TRUE;
+}
+
+void
+qcdm_result_add_uint32 (QCDMResult *result,
+ const char *key,
+ guint32 num)
+{
+ GValue *val;
+
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (result->refcount > 0);
+ g_return_if_fail (key != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, num);
+
+ g_hash_table_insert (result->hash, (gpointer) key, val);
+}
+
+gboolean
+qcdm_result_get_uint32 (QCDMResult *result,
+ const char *key,
+ guint32 *out_val)
+{
+ GValue *val;
+
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (result->refcount > 0, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (out_val != NULL, FALSE);
+
+ val = g_hash_table_lookup (result->hash, key);
+ if (!val)
+ return FALSE;
+
+ g_warn_if_fail (G_VALUE_HOLDS_UINT (val));
+ if (!G_VALUE_HOLDS_UINT (val))
+ return FALSE;
+
+ *out_val = (guint32) g_value_get_uint (val);
+ return TRUE;
+}
+
+void
+qcdm_result_add_boxed (QCDMResult *result,
+ const char *key,
+ GType btype,
+ gpointer boxed)
+{
+ GValue *val;
+
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (result->refcount > 0);
+ g_return_if_fail (key != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, btype);
+ g_value_set_static_boxed (val, boxed);
+
+ g_hash_table_insert (result->hash, (gpointer) key, val);
+}
+
+gboolean
+qcdm_result_get_boxed (QCDMResult *result,
+ const char *key,
+ gpointer *out_val)
+{
+ GValue *val;
+
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (result->refcount > 0, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (out_val != NULL, FALSE);
+
+ val = g_hash_table_lookup (result->hash, key);
+ if (!val)
+ return FALSE;
+
+ g_warn_if_fail (G_VALUE_HOLDS_BOXED (val));
+ if (!G_VALUE_HOLDS_BOXED (val))
+ return FALSE;
+
+ *out_val = g_value_get_boxed (val);
+ return TRUE;
+}
+
diff --git a/libqcdm/src/result.h b/libqcdm/src/result.h
new file mode 100644
index 0000000..4912b07
--- /dev/null
+++ b/libqcdm/src/result.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_RESULT_H
+#define LIBQCDM_RESULT_H
+
+#include <glib.h>
+
+typedef struct QCDMResult QCDMResult;
+
+gboolean qcdm_result_get_string (QCDMResult *result,
+ const char *key,
+ const char **out_val);
+
+gboolean qcdm_result_get_uint8 (QCDMResult *result,
+ const char *key,
+ guint8 *out_val);
+
+gboolean qcdm_result_get_uint32 (QCDMResult *result,
+ const char *key,
+ guint32 *out_val);
+
+QCDMResult *qcdm_result_ref (QCDMResult *result);
+
+void qcdm_result_unref (QCDMResult *result);
+
+#endif /* LIBQCDM_RESULT_H */
+
diff --git a/libqcdm/src/utils.c b/libqcdm/src/utils.c
new file mode 100644
index 0000000..b581548
--- /dev/null
+++ b/libqcdm/src/utils.c
@@ -0,0 +1,311 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "utils.h"
+
+/* QCDM protocol frames are pseudo Async HDLC frames which end with a 3-byte
+ * trailer. This trailer consists of the 16-bit CRC of the frame plus an ending
+ * "async control character" whose value is 0x7E. The frame *and* the CRC are
+ * escaped before adding the trailing control character so that the control
+ * character (0x7E) and the escape marker (0x7D) are never seen in the frame.
+ */
+
+/* Table of CRCs for each possible byte, with a generator polynomial of 0x8408 */
+const guint16 crc_table[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/* Calculate the CRC for a buffer using a seed of 0xffff */
+guint16
+crc16 (const char *buffer, gsize len)
+{
+ guint16 crc = 0xffff;
+
+ while (len--)
+ crc = crc_table[(crc ^ *buffer++) & 0xff] ^ (crc >> 8);
+ return ~crc;
+}
+
+#define DIAG_ESC_CHAR 0x7D /* Escape sequence 1st character value */
+#define DIAG_ESC_MASK 0x20 /* Escape sequence complement value */
+
+/* Performs DM escaping on inbuf putting the result into outbuf, and returns
+ * the final length of the buffer.
+ */
+gsize
+dm_escape (const char *inbuf,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len)
+{
+ const char *src = inbuf;
+ char *dst = outbuf;
+ size_t i = inbuf_len;
+
+ g_return_val_if_fail (inbuf != NULL, 0);
+ g_return_val_if_fail (inbuf_len > 0, 0);
+ g_return_val_if_fail (outbuf != NULL, 0);
+ g_return_val_if_fail (outbuf_len > inbuf_len, 0);
+
+ /* Since escaping potentially doubles the # of bytes, short-circuit the
+ * length check if destination buffer is clearly large enough. Note the
+ *
+ */
+ if (outbuf_len <= inbuf_len << 1) {
+ size_t outbuf_required = inbuf_len + 1; /* +1 for the trailing control char */
+
+ /* Each escaped character takes up two bytes in the output buffer */
+ while (i--) {
+ if (*src == DIAG_CONTROL_CHAR || *src == DIAG_ESC_CHAR)
+ outbuf_required++;
+ src++;
+ }
+
+ if (outbuf_len < outbuf_required)
+ return 0;
+ }
+
+ /* Do the actual escaping. Replace both the control character and
+ * the escape character in the source buffer with the following sequence:
+ *
+ * <escape_char> <src_byte ^ escape_mask>
+ */
+ src = inbuf;
+ i = inbuf_len;
+ while (i--) {
+ if (*src == DIAG_CONTROL_CHAR || *src == DIAG_ESC_CHAR) {
+ *dst++ = DIAG_ESC_CHAR;
+ *dst++ = *src ^ DIAG_ESC_MASK;
+ } else
+ *dst++ = *src;
+ src++;
+ }
+
+ return (dst - outbuf);
+}
+
+gsize
+dm_unescape (const char *inbuf,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len,
+ gboolean *escaping)
+{
+ size_t i, outsize;
+
+ g_return_val_if_fail (inbuf_len > 0, 0);
+ g_return_val_if_fail (outbuf_len >= inbuf_len, 0);
+ g_return_val_if_fail (escaping != NULL, 0);
+
+ for (i = 0, outsize = 0; i < inbuf_len; i++) {
+ if (*escaping) {
+ outbuf[outsize++] = inbuf[i] ^ DIAG_ESC_MASK;
+ *escaping = FALSE;
+ } else if (inbuf[i] == DIAG_ESC_CHAR)
+ *escaping = TRUE;
+ else
+ outbuf[outsize++] = inbuf[i];
+
+ /* About to overrun output buffer size */
+ if (outsize >= outbuf_len)
+ return 0;
+ }
+
+ return outsize;
+}
+
+/**
+ * dm_encapsulate_buffer:
+ * @inbuf: buffer in which a valid QCDM packet exists
+ * @cmd_len: size of the QCDM packet contained in @inbuf
+ * @inbuf_len: total size of @inbuf itself (not just the packet)
+ * @outbuf: buffer in which to put the encapsulated QCDM packet
+ * @outbuf_len: total size of @outbuf
+ *
+ * Escapes and CRCs a QCDM packet, and finally adds the trailing control
+ * character that denotes the end of the QCDM packet.
+ *
+ * Returns: size of the encapsulated QCDM command writted to @outbuf.
+ **/
+gsize
+dm_encapsulate_buffer (char *inbuf,
+ gsize cmd_len,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len)
+{
+ guint16 crc;
+ gsize escaped_len;
+
+ g_return_val_if_fail (inbuf != NULL, 0);
+ g_return_val_if_fail (cmd_len >= 1, 0);
+ g_return_val_if_fail (inbuf_len >= cmd_len + 2, 0); /* space for CRC */
+ g_return_val_if_fail (outbuf != NULL, 0);
+
+ /* Add the CRC */
+ crc = crc16 (inbuf, cmd_len);
+ inbuf[cmd_len++] = crc & 0xFF;
+ inbuf[cmd_len++] = (crc >> 8) & 0xFF;
+
+ escaped_len = dm_escape (inbuf, cmd_len, outbuf, outbuf_len);
+ g_return_val_if_fail (outbuf_len > escaped_len, 0);
+ outbuf[escaped_len++] = DIAG_CONTROL_CHAR;
+
+ return escaped_len;
+}
+
+/**
+ * dm_decapsulate_buffer:
+ * @inbuf: buffer in which to look for a QCDM packet
+ * @inbuf_len: length of valid data in @inbuf
+ * @outbuf: buffer in which to put decapsulated QCDM packet
+ * @outbuf_len: max size of @outbuf
+ * @out_decap_len: on success, size of the decapsulated QCDM packet
+ * @out_used: on either success or failure, amount of data used; caller should
+ * discard this much data from @inbuf before the next call to this function
+ * @out_need_more: when TRUE, indicates that more data is required before
+ * and determination about a valid QCDM packet can be made; caller should add
+ * more data to @inbuf before calling this function again.
+ *
+ * Attempts to retrieve, unescape, and CRC-check a QCDM packet from the given
+ * buffer.
+ *
+ * Returns: FALSE on error (packet was invalid or malformed, or the CRC check
+ * failed, etc) and places number of bytes to discard from @inbuf in @out_used.
+ * When TRUE, either more data is required (in which case @out_need_more will
+ * be TRUE), or a QCDM packet was successfully retrieved from @inbuf and the
+ * decapsulated packet of length @out_decap_len was placed into @outbuf. In
+ * all cases the caller should advance the buffer by the number of bytes
+ * returned in @out_used before calling this function again.
+ **/
+gboolean
+dm_decapsulate_buffer (const char *inbuf,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len,
+ gsize *out_decap_len,
+ gsize *out_used,
+ gboolean *out_need_more)
+{
+ gboolean escaping = FALSE;
+ gsize i, pkt_len = 0, unesc_len;
+ guint16 crc, pkt_crc;
+
+ g_return_val_if_fail (inbuf != NULL, FALSE);
+ g_return_val_if_fail (outbuf != NULL, FALSE);
+ g_return_val_if_fail (outbuf_len > 0, FALSE);
+ g_return_val_if_fail (out_decap_len != NULL, FALSE);
+ g_return_val_if_fail (out_used != NULL, FALSE);
+ g_return_val_if_fail (out_need_more != NULL, FALSE);
+
+ *out_decap_len = 0;
+ *out_used = 0;
+ *out_need_more = FALSE;
+
+ if (inbuf_len < 4) {
+ *out_need_more = TRUE;
+ return TRUE;
+ }
+
+ /* Find the async control character */
+ for (i = 0; i < inbuf_len; i++) {
+ if (inbuf[i] == DIAG_CONTROL_CHAR) {
+ /* If the control character shows up in a position before a valid
+ * QCDM packet length (4), the packet is malformed.
+ */
+ if (i < 3) {
+ /* Tell the caller to advance the buffer past the control char */
+ *out_used = i + 1;
+ return FALSE;
+ }
+
+ pkt_len = i;
+ break;
+ }
+ }
+
+ /* No control char yet, need more data */
+ if (!pkt_len) {
+ *out_need_more = TRUE;
+ return TRUE;
+ }
+
+ /* Unescape first; note that pkt_len */
+ unesc_len = dm_unescape (inbuf, pkt_len, outbuf, outbuf_len, &escaping);
+ if (!unesc_len) {
+ /* Tell the caller to advance the buffer past the control char */
+ *out_used = pkt_len + 1;
+ return FALSE;
+ }
+
+ if (escaping) {
+ *out_need_more = TRUE;
+ return TRUE;
+ }
+
+ /* Check the CRC of the packet's data */
+ crc = crc16 (outbuf, unesc_len - 2);
+ pkt_crc = outbuf[unesc_len - 2] & 0xFF;
+ pkt_crc |= (outbuf[unesc_len - 1] & 0xFF) << 8;
+ if (crc != pkt_crc) {
+ *out_used = pkt_len + 1; /* packet + CRC + 0x7E */
+ return FALSE;
+ }
+
+ *out_used = pkt_len + 1; /* packet + CRC + 0x7E */
+ *out_decap_len = unesc_len - 2; /* decap_len should not include the CRC */
+ return TRUE;
+}
+
diff --git a/libqcdm/src/utils.h b/libqcdm/src/utils.h
new file mode 100644
index 0000000..5fccf7f
--- /dev/null
+++ b/libqcdm/src/utils.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <glib.h>
+
+#define DIAG_CONTROL_CHAR 0x7E
+#define DIAG_TRAILER_LEN 3
+
+guint16 crc16 (const char *buffer, gsize len);
+
+gsize dm_escape (const char *inbuf,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len);
+
+gsize dm_unescape (const char *inbuf,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len,
+ gboolean *escaping);
+
+gsize dm_encapsulate_buffer (char *inbuf,
+ gsize cmd_len,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len);
+
+gboolean dm_decapsulate_buffer (const char *inbuf,
+ gsize inbuf_len,
+ char *outbuf,
+ gsize outbuf_len,
+ gsize *out_decap_len,
+ gsize *out_used,
+ gboolean *out_need_more);
+
+#endif /* UTILS_H */
+
diff --git a/libqcdm/tests/Makefile.am b/libqcdm/tests/Makefile.am
new file mode 100644
index 0000000..eb38fdb
--- /dev/null
+++ b/libqcdm/tests/Makefile.am
@@ -0,0 +1,32 @@
+INCLUDES = \
+ -I$(top_srcdir)/libqcdm/src
+
+noinst_PROGRAMS = test-qcdm
+
+test_qcdm_SOURCES = \
+ test-qcdm-crc.c \
+ test-qcdm-crc.h \
+ test-qcdm-escaping.c \
+ test-qcdm-escaping.h \
+ test-qcdm-utils.c \
+ test-qcdm-utils.h \
+ test-qcdm-com.c \
+ test-qcdm-com.h \
+ test-qcdm-result.c \
+ test-qcdm-result.h \
+ test-qcdm.c
+
+test_qcdm_CPPFLAGS = \
+ $(MM_CFLAGS)
+
+test_qcdm_LDADD = \
+ $(top_builddir)/libqcdm/src/libqcdm.la \
+ $(MM_LIBS)
+
+if WITH_TESTS
+
+check-local: test-qcdm
+ $(abs_builddir)/test-qcdm
+
+endif
+
diff --git a/libqcdm/tests/test-qcdm-com.c b/libqcdm/tests/test-qcdm-com.c
new file mode 100644
index 0000000..f41d249
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-com.c
@@ -0,0 +1,1184 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "test-qcdm-com.h"
+#include "com.h"
+#include "utils.h"
+#include "result.h"
+#include "commands.h"
+#include "error.h"
+
+/************************************************************/
+
+static const char *
+prev_to_string (guint8 prev)
+{
+ switch (prev) {
+ case QCDM_CDMA_PREV_IS_95:
+ return "IS-95";
+ case QCDM_CDMA_PREV_IS_95A:
+ return "IS-95A";
+ case QCDM_CDMA_PREV_IS_95A_TSB74:
+ return "IS-95A TSB-74";
+ case QCDM_CDMA_PREV_IS_95B_PHASE1:
+ return "IS-95B Phase I";
+ case QCDM_CDMA_PREV_IS_95B_PHASE2:
+ return "IS-95B Phase II";
+ case QCDM_CDMA_PREV_IS2000_REL0:
+ return "IS-2000 Release 0";
+ case QCDM_CDMA_PREV_IS2000_RELA:
+ return "IS-2000 Release A";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static const char *
+band_class_to_string (guint8 band_class)
+{
+ switch (band_class) {
+ case QCDM_CDMA_BAND_CLASS_0_CELLULAR_800:
+ return "0 (Cellular 800)";
+ case QCDM_CDMA_BAND_CLASS_1_PCS:
+ return "1 (PCS 1900)";
+ case QCDM_CDMA_BAND_CLASS_2_TACS:
+ return "2 (TACS)";
+ case QCDM_CDMA_BAND_CLASS_3_JTACS:
+ return "3 (JTACS)";
+ case QCDM_CDMA_BAND_CLASS_4_KOREAN_PCS:
+ return "4 (Korean PCS)";
+ case QCDM_CDMA_BAND_CLASS_5_NMT450:
+ return "5 (NMT-450)";
+ case QCDM_CDMA_BAND_CLASS_6_IMT2000:
+ return "6 (IMT-2000)";
+ case QCDM_CDMA_BAND_CLASS_7_CELLULAR_700:
+ return "7 (Cellular 700)";
+ case QCDM_CDMA_BAND_CLASS_8_1800:
+ return "8 (1800 MHz)";
+ case QCDM_CDMA_BAND_CLASS_9_900:
+ return "9 (1900 MHz)";
+ case QCDM_CDMA_BAND_CLASS_10_SECONDARY_800:
+ return "10 (Secondary 800 MHz)";
+ case QCDM_CDMA_BAND_CLASS_11_PAMR_400:
+ return "11 (PAMR 400)";
+ case QCDM_CDMA_BAND_CLASS_12_PAMR_800:
+ return "11 (PAMR 800)";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static const char *
+hdr_rev_to_string (guint8 hdr_rev)
+{
+ switch (hdr_rev) {
+ case QCDM_HDR_REV_0:
+ return "0";
+ case QCDM_HDR_REV_A:
+ return "A";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+/************************************************************/
+
+typedef struct {
+ char *port;
+ int fd;
+ struct termios old_t;
+ gboolean debug;
+} TestComData;
+
+gpointer
+test_com_setup (const char *port)
+{
+ TestComData *d;
+ int ret;
+
+ d = g_malloc0 (sizeof (TestComData));
+ g_assert (d);
+
+ if (getenv ("SERIAL_DEBUG"))
+ d->debug = TRUE;
+
+ errno = 0;
+ d->fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+ if (d->fd < 0)
+ g_warning ("%s: open failed: (%d) %s", port, errno, strerror (errno));
+ g_assert (d->fd >= 0);
+
+ ret = ioctl (d->fd, TIOCEXCL);
+ if (ret) {
+ g_warning ("%s: lock failed: (%d) %s", port, errno, strerror (errno));
+ close (d->fd);
+ d->fd = -1;
+ }
+ g_assert (ret == 0);
+
+ ret = ioctl (d->fd, TCGETA, &d->old_t);
+ if (ret) {
+ g_warning ("%s: old termios failed: (%d) %s", port, errno, strerror (errno));
+ close (d->fd);
+ d->fd = -1;
+ }
+ g_assert (ret == 0);
+
+ d->port = g_strdup (port);
+ return d;
+}
+
+void
+test_com_teardown (gpointer user_data)
+{
+ TestComData *d = user_data;
+
+ g_assert (d);
+
+ g_free (d->port);
+ close (d->fd);
+ g_free (d);
+}
+
+static void
+print_buf (const char *detail, const char *buf, gsize len)
+{
+ int i = 0;
+ gboolean newline = FALSE;
+
+ g_print ("%s (%zu) ", detail, len);
+ for (i = 0; i < len; i++) {
+ g_print ("0x%02x ", buf[i] & 0xFF);
+ if (((i + 1) % 12) == 0) {
+ g_print ("\n");
+ newline = TRUE;
+ } else
+ newline = FALSE;
+ }
+
+ if (!newline)
+ g_print ("\n");
+}
+
+static gboolean
+send_command (TestComData *d, char *buf, gsize len)
+{
+ int status;
+ int eagain_count = 1000;
+ gsize i = 0;
+
+ if (d->debug)
+ print_buf (">>>", buf, len);
+
+ while (i < len) {
+ errno = 0;
+ status = write (d->fd, &buf[i], 1);
+ if (status < 0) {
+ if (errno == EAGAIN) {
+ eagain_count--;
+ if (eagain_count <= 0)
+ return FALSE;
+ } else
+ g_assert (errno == 0);
+ } else
+ i++;
+
+ usleep (1000);
+ }
+
+ return TRUE;
+}
+
+static gsize
+wait_reply (TestComData *d, char *buf, gsize len)
+{
+ fd_set in;
+ int result;
+ struct timeval timeout = { 1, 0 };
+ char readbuf[1024];
+ ssize_t bytes_read;
+ int total = 0, retries = 0;
+ gsize decap_len = 0;
+
+ FD_ZERO (&in);
+ FD_SET (d->fd, &in);
+ result = select (d->fd + 1, &in, NULL, NULL, &timeout);
+ if (result != 1 || !FD_ISSET (d->fd, &in))
+ return 0;
+
+ do {
+ errno = 0;
+ bytes_read = read (d->fd, &readbuf[total], 1);
+ if ((bytes_read == 0) || (errno == EAGAIN)) {
+ /* Haven't gotten the async control char yet */
+ if (retries > 20)
+ return 0; /* 2 seconds, give up */
+
+ /* Otherwise wait a bit and try again */
+ usleep (100000);
+ retries++;
+ continue;
+ } else if (bytes_read == 1) {
+ gboolean more = FALSE, success;
+ gsize used = 0;
+
+ total++;
+ decap_len = 0;
+ success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more);
+
+ /* Discard used data */
+ if (used > 0) {
+ total -= used;
+ memmove (readbuf, &readbuf[used], total);
+ }
+
+ if (success && !more) {
+ /* Success; we have a packet */
+ break;
+ }
+ } else {
+ /* Some error occurred */
+ return 0;
+ }
+ } while (total < sizeof (readbuf));
+
+ if (d->debug) {
+ print_buf ("<<<", readbuf, total);
+ print_buf ("D<<", buf, decap_len);
+ }
+
+ return decap_len;
+}
+
+void
+test_com_port_init (void *f, void *data)
+{
+ TestComData *d = data;
+ GError *error = NULL;
+ gboolean success;
+
+ success = qcdm_port_setup (d->fd, &error);
+ if (!success) {
+ g_warning ("%s: error setting up port: (%d) %s",
+ d->port,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ }
+ g_assert (success);
+}
+
+void
+test_com_version_info (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[512];
+ const char *str;
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+
+ len = qcdm_cmd_version_info_new (buf, sizeof (buf), NULL);
+ g_assert (len == 4);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_version_info_result (buf, reply_len, &error);
+ g_assert (result);
+
+ g_print ("\n");
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_DATE, &str);
+ g_message ("%s: Compiled Date: %s", __func__, str);
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_COMP_TIME, &str);
+ g_message ("%s: Compiled Time: %s", __func__, str);
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_DATE, &str);
+ g_message ("%s: Release Date: %s", __func__, str);
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_RELEASE_TIME, &str);
+ g_message ("%s: Release Time: %s", __func__, str);
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_VERSION_INFO_ITEM_MODEL, &str);
+ g_message ("%s: Model: %s", __func__, str);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_esn (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[512];
+ const char *str;
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+
+ len = qcdm_cmd_esn_new (buf, sizeof (buf), NULL);
+ g_assert (len == 4);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_esn_result (buf, reply_len, &error);
+ g_assert (result);
+
+ g_print ("\n");
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_ESN_ITEM_ESN, &str);
+ g_message ("%s: ESN: %s", __func__, str);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_mdn (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[512];
+ const char *str;
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+
+ len = qcdm_cmd_nv_get_mdn_new (buf, sizeof (buf), 0, NULL);
+ g_assert (len > 0);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_nv_get_mdn_result (buf, reply_len, &error);
+ if (!result) {
+ g_assert_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_NVCMD_FAILED);
+ return;
+ }
+
+ g_print ("\n");
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_NV_GET_MDN_ITEM_MDN, &str);
+ g_message ("%s: MDN: %s", __func__, str);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_read_roam_pref (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[512];
+ guint8 pref;
+ const char *msg;
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+
+ len = qcdm_cmd_nv_get_roam_pref_new (buf, sizeof (buf), 0, NULL);
+ g_assert (len > 0);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_nv_get_roam_pref_result (buf, reply_len, &error);
+ g_assert (result);
+
+ g_print ("\n");
+
+ success = qcdm_result_get_uint8 (result, QCDM_CMD_NV_GET_ROAM_PREF_ITEM_ROAM_PREF, &pref);
+ g_assert (success);
+
+ switch (pref) {
+ case QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_HOME_ONLY:
+ msg = "home only";
+ break;
+ case QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_ROAM_ONLY:
+ msg = "roaming only";
+ break;
+ case QCDM_CMD_NV_ROAM_PREF_ITEM_ROAM_PREF_AUTO:
+ msg = "automatic";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ g_message ("%s: Roam preference: 0x%02X (%s)", __func__, pref, msg);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_read_mode_pref (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[512];
+ guint8 pref;
+ const char *msg;
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+
+ len = qcdm_cmd_nv_get_mode_pref_new (buf, sizeof (buf), 0, NULL);
+ g_assert (len > 0);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_nv_get_mode_pref_result (buf, reply_len, &error);
+ if (!result) {
+ g_assert_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_NVCMD_FAILED);
+ return;
+ }
+
+ g_print ("\n");
+
+ success = qcdm_result_get_uint8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, &pref);
+ g_assert (success);
+
+ switch (pref) {
+ case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_ONLY:
+ msg = "1X only";
+ break;
+ case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_HDR_ONLY:
+ msg = "HDR only";
+ break;
+ case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO:
+ msg = "automatic";
+ break;
+ default:
+ msg = "unknown";
+ break;
+ }
+ g_message ("%s: Mode preference: 0x%02X (%s)", __func__, pref, msg);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_status (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[100];
+ const char *str, *detail;
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+ guint32 n32;
+ guint8 n8;
+
+ len = qcdm_cmd_cdma_status_new (buf, sizeof (buf), NULL);
+ g_assert (len == 4);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_cdma_status_result (buf, reply_len, &error);
+ g_assert (result);
+
+ g_print ("\n");
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_CDMA_STATUS_ITEM_ESN, &str);
+ g_message ("%s: ESN: %s", __func__, str);
+
+ n32 = 0;
+ detail = NULL;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE, &n32);
+ switch (n32) {
+ case QCDM_CMD_CDMA_STATUS_RF_MODE_ANALOG:
+ detail = "analog";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_CELLULAR:
+ detail = "CDMA cellular";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RF_MODE_CDMA_PCS:
+ detail = "CDMA PCS";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RF_MODE_SLEEP:
+ detail = "sleep";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RF_MODE_GPS:
+ detail = "GPS";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RF_MODE_HDR:
+ detail = "HDR";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: CDMA RF Mode: %u (%s)", __func__, n32, detail);
+
+ n32 = 0;
+ detail = NULL;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &n32);
+ switch (n32) {
+ case QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA:
+ detail = "entering CDMA";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RX_STATE_SYNC_CHANNEL:
+ detail = "sync channel";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RX_STATE_PAGING_CHANNEL:
+ detail = "paging channel";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL_INIT:
+ detail = "traffic channel init";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RX_STATE_TRAFFIC_CHANNEL:
+ detail = "traffic channel";
+ break;
+ case QCDM_CMD_CDMA_STATUS_RX_STATE_EXITING_CDMA:
+ detail = "exiting CDMA";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: CDMA RX State: %u (%s)", __func__, n32, detail);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON, &n32);
+ g_message ("%s: Entry Reason: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL, &n32);
+ g_message ("%s: Current Channel: %u", __func__, n32);
+
+ n8 = 0;
+ qcdm_result_get_uint8 (result, QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL, &n8);
+ g_message ("%s: Code Channel: %u", __func__, n8);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE, &n32);
+ g_message ("%s: Pilot Base: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &n32);
+ g_message ("%s: CDMA System ID: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_NID, &n32);
+ g_message ("%s: CDMA Network ID: %u", __func__, n32);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_sw_version (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[100];
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+
+ len = qcdm_cmd_sw_version_new (buf, sizeof (buf), NULL);
+ g_assert (len == 4);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_sw_version_result (buf, reply_len, &error);
+
+ /* Recent devices don't appear to implement this command */
+ g_assert (result == NULL);
+ g_assert_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND);
+
+/*
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_SW_VERSION_ITEM_VERSION, &str);
+ g_message ("%s: SW Version: %s", __func__, str);
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_DATE, &str);
+ g_message ("%s: Compiled Date: %s", __func__, str);
+
+ str = NULL;
+ qcdm_result_get_string (result, QCDM_CMD_SW_VERSION_ITEM_COMP_TIME, &str);
+ g_message ("%s: Compiled Time: %s", __func__, str);
+
+ qcdm_result_unref (result);
+*/
+}
+
+void
+test_com_pilot_sets (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[256];
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+ guint32 num, i;
+
+ len = qcdm_cmd_pilot_sets_new (buf, sizeof (buf), NULL);
+ g_assert (len == 4);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_pilot_sets_result (buf, reply_len, &error);
+ g_assert (result);
+
+ num = 0;
+ qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, &num);
+ g_message ("%s: Active Pilots: %d", __func__, num);
+ for (i = 0; i < num; i++) {
+ guint32 pn_offset = 0, ecio = 0;
+ float db = 0;
+
+ qcdm_cmd_pilot_sets_result_get_pilot (result,
+ QCDM_CMD_PILOT_SETS_TYPE_ACTIVE,
+ i,
+ &pn_offset,
+ &ecio,
+ &db);
+ g_message (" %d: PN offset %d", i, pn_offset);
+ g_message (" EC/IO %d (%.1f dB)", ecio, db);
+ }
+
+ num = 0;
+ qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_CANDIDATE, &num);
+ g_message ("%s: Candidate Pilots: %d", __func__, num);
+
+ num = 0;
+ qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_NEIGHBOR, &num);
+ g_message ("%s: Neighbor Pilots: %d", __func__, num);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_cm_subsys_state_info (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[100];
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+ guint32 n32;
+ const char *detail;
+
+ len = qcdm_cmd_cm_subsys_state_info_new (buf, sizeof (buf), NULL);
+ g_assert (len == 7);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_cm_subsys_state_info_result (buf, reply_len, &error);
+ g_assert (result);
+
+ g_print ("\n");
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE, &n32);
+ g_message ("%s: Call State: %u", __func__, n32);
+
+ n32 = 0;
+ detail = NULL;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &n32);
+ switch (n32) {
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE:
+ detail = "online";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Operating Mode: %u (%s)", __func__, n32, detail);
+
+ n32 = 0;
+ detail = NULL;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &n32);
+ switch (n32) {
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE:
+ detail = "no service";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS:
+ detail = "AMPS";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA:
+ detail = "CDMA";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR:
+ detail = "HDR/EVDO";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA:
+ detail = "WCDMA";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: System Mode: %u (%s)", __func__, n32, detail);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF, &n32);
+ switch (n32) {
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_DIGITAL_ONLY:
+ detail = "digital only";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_AUTO:
+ detail = "automatic";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_1X_ONLY:
+ detail = "1X only";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_MODE_PREF_HDR_ONLY:
+ detail = "HDR only";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Mode Preference: 0x%02X (%s)", __func__, n32 & 0xFF, detail);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF, &n32);
+ g_message ("%s: Band Preference: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF, &n32);
+ switch (n32) {
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_HOME_ONLY:
+ detail = "home only";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_ROAM_ONLY:
+ detail = "roaming only";
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_ROAM_PREF_AUTO:
+ detail = "automatic";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ g_message ("%s: Roam Preference: 0x%02X (%s)", __func__, n32 & 0xFF, detail);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF, &n32);
+ g_message ("%s: Service Domain Preference: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF, &n32);
+ g_message ("%s: Acquisition Order Preference: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, &n32);
+ g_message ("%s: Hybrid Preference: %u", __func__, n32);
+
+ n32 = 0;
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF, &n32);
+ g_message ("%s: Network Selection Preference: %u", __func__, n32);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_hdr_subsys_state_info (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[100];
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+ guint8 num;
+ const char *detail;
+
+ len = qcdm_cmd_hdr_subsys_state_info_new (buf, sizeof (buf), NULL);
+ g_assert (len == 7);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ g_print ("\n");
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_hdr_subsys_state_info_result (buf, reply_len, &error);
+ if (!result) {
+ /* 1x-only devices won't implement the HDR subsystem of course */
+ g_assert_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND);
+ g_message ("%s: device does not implement the HDR subsystem", __func__);
+ return;
+ }
+ g_assert (result);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_AT_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACQUISITION:
+ detail = "acquisition";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_SYNC:
+ detail = "sync";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_IDLE:
+ detail = "idle";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_ACCESS:
+ detail = "access";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_AT_STATE_CONNECTED:
+ detail = "connected";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: AT State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED:
+ detail = "closed";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_SETUP:
+ detail = "setup";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AT_INIT:
+ detail = "AT init";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_AN_INIT:
+ detail = "AN init";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN:
+ detail = "open";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSING:
+ detail = "closing";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Session State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INIT:
+ detail = "init";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE:
+ detail = "idle";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED:
+ detail = "connected";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: ALMP State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_INIT_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_NET_DETERMINE:
+ detail = "searching";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_ACQUISITION:
+ detail = "acquisition";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_INIT_STATE_SYNC:
+ detail = "sync";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Init State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_IDLE_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SLEEP:
+ detail = "sleep";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_MONITOR:
+ detail = "monitor";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_IDLE_STATE_SETUP:
+ detail = "setup";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Idle State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_CONNECTED_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_OPEN:
+ detail = "open";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_CONNECTED_STATE_CLOSING:
+ detail = "closing";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Connected State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ROUTE_UPDATE_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_IDLE:
+ detail = "idle";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_ROUTE_UPDATE_STATE_CONNECTED:
+ detail = "connected";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Route Update State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ detail = NULL;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_OVERHEAD_MSG_STATE, &num);
+ switch (num) {
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INIT:
+ detail = "initial";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_INACTIVE:
+ detail = "inactive";
+ break;
+ case QCDM_CMD_HDR_SUBSYS_STATE_INFO_OVERHEAD_MSG_STATE_ACTIVE:
+ detail = "active";
+ break;
+ default:
+ detail = "unknown";
+ break;
+ }
+ g_message ("%s: Overhead Msg State: %u (%s)", __func__, num, detail);
+
+ num = 0;
+ qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE, &num);
+ g_message ("%s: HDR Hybrid Mode: %u", __func__, num);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_zte_subsys_status (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[100];
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+ guint8 ind = 0;
+
+ len = qcdm_cmd_zte_subsys_status_new (buf, sizeof (buf), NULL);
+ g_assert (len == 7);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ g_print ("\n");
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_zte_subsys_status_result (buf, reply_len, &error);
+ if (!result) {
+ /* Obviously not all devices implement this command */
+ g_assert_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND);
+ g_message ("%s: device does not implement the ZTE subsystem", __func__);
+ return;
+ }
+ g_assert (result);
+
+ qcdm_result_get_uint8 (result, QCDM_CMD_ZTE_SUBSYS_STATUS_ITEM_SIGNAL_INDICATOR, &ind);
+ g_message ("%s: Signal Indicator: %d", __func__, ind);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_com_nw_subsys_modem_snapshot_cdma (void *f, void *data)
+{
+ TestComData *d = data;
+ gboolean success;
+ GError *error = NULL;
+ char buf[200];
+ gint len;
+ QCDMResult *result;
+ gsize reply_len;
+ guint8 num8 = 0;
+ guint32 num32 = 0;
+
+ len = qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (buf, sizeof (buf), QCDM_NW_CHIPSET_6800, NULL);
+ g_assert (len == 12);
+
+ /* Send the command */
+ success = send_command (d, buf, len);
+ g_assert (success);
+
+ /* Get a response */
+ reply_len = wait_reply (d, buf, sizeof (buf));
+
+ g_print ("\n");
+
+ /* Parse the response into a result structure */
+ result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (buf, reply_len, &error);
+ if (!result) {
+ /* Obviously not all devices implement this command */
+ g_assert_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_COMMAND);
+ return;
+ }
+ g_assert (result);
+
+ qcdm_result_get_uint32 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_RSSI, &num32);
+ g_message ("%s: RSSI: %d", __func__, num32);
+
+ qcdm_result_get_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_PREV, &num8);
+ g_message ("%s: P_REV: %s", __func__, prev_to_string (num8));
+
+ qcdm_result_get_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_BAND_CLASS, &num8);
+ g_message ("%s: Band Class: %s", __func__, band_class_to_string (num8));
+
+ qcdm_result_get_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI, &num8);
+ g_message ("%s: ERI: %d", __func__, num8);
+
+ qcdm_result_get_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &num8);
+ g_message ("%s: HDR Revision: %s", __func__, hdr_rev_to_string (num8));
+
+ qcdm_result_unref (result);
+}
+
diff --git a/libqcdm/tests/test-qcdm-com.h b/libqcdm/tests/test-qcdm-com.h
new file mode 100644
index 0000000..3cf7c4f
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-com.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEST_QCDM_COM_H
+#define TEST_QCDM_COM_H
+
+gpointer test_com_setup (const char *port);
+void test_com_teardown (gpointer d);
+
+void test_com_port_init (void *f, void *data);
+
+void test_com_version_info (void *f, void *data);
+
+void test_com_esn (void *f, void *data);
+
+void test_com_mdn (void *f, void *data);
+
+void test_com_read_roam_pref (void *f, void *data);
+
+void test_com_read_mode_pref (void *f, void *data);
+
+void test_com_status (void *f, void *data);
+
+void test_com_sw_version (void *f, void *data);
+
+void test_com_pilot_sets (void *f, void *data);
+
+void test_com_cm_subsys_state_info (void *f, void *data);
+
+void test_com_hdr_subsys_state_info (void *f, void *data);
+
+void test_com_zte_subsys_status (void *f, void *data);
+
+void test_com_nw_subsys_modem_snapshot_cdma (void *f, void *data);
+
+#endif /* TEST_QCDM_COM_H */
+
diff --git a/libqcdm/tests/test-qcdm-crc.c b/libqcdm/tests/test-qcdm-crc.c
new file mode 100644
index 0000000..0cb3e86
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-crc.c
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-qcdm-crc.h"
+#include "utils.h"
+
+void
+test_crc16_2 (void *f, void *data)
+{
+ static const char buf[] = {
+ 0x26, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ };
+ guint16 crc;
+ guint16 expected = 0x6D69;
+
+ /* CRC check */
+ crc = crc16 (buf, sizeof (buf));
+ g_assert (crc == expected);
+}
+
+void
+test_crc16_1 (void *f, void *data)
+{
+ static const char buf[] = {
+ 0x4b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+ };
+ guint16 crc;
+ guint16 expected = 0x097A;
+
+ /* CRC check */
+ crc = crc16 (buf, sizeof (buf));
+ g_assert (crc == expected);
+}
+
diff --git a/libqcdm/tests/test-qcdm-crc.h b/libqcdm/tests/test-qcdm-crc.h
new file mode 100644
index 0000000..91b95a0
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-crc.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEST_QCDM_CRC_H
+#define TEST_QCDM_CRC_H
+
+void test_crc16_2 (void *f, void *data);
+void test_crc16_1 (void *f, void *data);
+
+#endif /* TEST_QCDM_CRC_H */
+
diff --git a/libqcdm/tests/test-qcdm-escaping.c b/libqcdm/tests/test-qcdm-escaping.c
new file mode 100644
index 0000000..fb5fb82
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-escaping.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-qcdm-escaping.h"
+#include "utils.h"
+
+static const char data1[] = {
+ 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
+ 0x0a, 0x6e, 0x6f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x74, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0a, 0x70, 0x68, 0x6f,
+ 0x6e, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d, 0x7e, 0x7d, 0x7e, 0x7d, 0x7e, 0x6e,
+ 0x6b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x0a, 0x6f, 0x70,
+ 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x6c,
+ 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
+ 0x74, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e,
+ 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66,
+ 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b,
+ 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d,
+ 0x6e, 0x6f, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x0a,
+ 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x69,
+ 0x6c, 0x75, 0x72, 0x65, 0x0a, 0x73, 0x69, 0x6d, 0x62, 0x75, 0x73, 0x79,
+ 0x0a, 0x73, 0x69, 0x6d, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x0a, 0x69, 0x6e,
+ 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x70, 0x61, 0x73, 0x73, 0x77,
+ 0x6f, 0x72, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x32, 0x72,
+ 0x65, 0x71, 0x75, 0x69
+};
+
+static const char expected1[] = {
+ 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
+ 0x0a, 0x6e, 0x6f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x74, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0a, 0x70, 0x68, 0x6f,
+ 0x6e, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5d, 0x7d,
+ 0x5e, 0x7d, 0x5d, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5e, 0x6e, 0x6b, 0x72,
+ 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x6c, 0x6c, 0x6f,
+ 0x77, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x6e, 0x6f, 0x74, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
+ 0x64, 0x0a, 0x70, 0x68, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69,
+ 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
+ 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x6e, 0x6f,
+ 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x0a, 0x73, 0x69,
+ 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
+ 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71, 0x75, 0x69,
+ 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x69, 0x6c, 0x75,
+ 0x72, 0x65, 0x0a, 0x73, 0x69, 0x6d, 0x62, 0x75, 0x73, 0x79, 0x0a, 0x73,
+ 0x69, 0x6d, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x0a, 0x69, 0x6e, 0x63, 0x6f,
+ 0x72, 0x72, 0x65, 0x63, 0x74, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
+ 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x32, 0x72, 0x65, 0x71,
+ 0x75, 0x69
+};
+
+void
+test_escape1 (void *f, void *data)
+{
+ char escaped[1024];
+ gsize len;
+
+ /* Ensure that escaping in general works */
+ len = dm_escape (data1, sizeof (data1), escaped, sizeof (escaped));
+ g_assert (len == 266);
+ g_assert (len == sizeof (expected1));
+ g_assert (memcmp (escaped, expected1, len) == 0);
+}
+
+static const char data2[] = {
+ 0x4b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+void
+test_escape2 (void *f, void *data)
+{
+ char escaped[1024];
+ gsize len;
+
+ /* Ensure that escaping data that doesn't need escaping works */
+ len = dm_escape (data2, sizeof (data2), escaped, sizeof (escaped));
+ g_assert (len == sizeof (data2));
+ g_assert (memcmp (escaped, data2, len) == 0);
+}
+
+void
+test_escape_unescape (void *f, void *data)
+{
+ char escaped[512];
+ char unescaped[512];
+ gsize len, unlen;
+ gboolean escaping = FALSE;
+
+ /* Ensure that escaping data that needs escaping, and then unescaping it,
+ * produces the exact same data as was originally escaped.
+ */
+ len = dm_escape (data1, sizeof (data1), escaped, sizeof (escaped));
+ unlen = dm_unescape (escaped, len, unescaped, sizeof (unescaped), &escaping);
+
+ g_assert (unlen == sizeof (data1));
+ g_assert (memcmp (unescaped, data1, unlen) == 0);
+}
+
diff --git a/libqcdm/tests/test-qcdm-escaping.h b/libqcdm/tests/test-qcdm-escaping.h
new file mode 100644
index 0000000..91706ee
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-escaping.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEST_QCDM_ESCAPING_H
+#define TEST_QCDM_ESCAPING_H
+
+void test_escape1 (void *f, void *data);
+void test_escape2 (void *f, void *data);
+void test_escape_unescape (void *f, void *data);
+
+#endif /* TEST_QCDM_ESCAPING_H */
+
diff --git a/libqcdm/tests/test-qcdm-result.c b/libqcdm/tests/test-qcdm-result.c
new file mode 100644
index 0000000..87f264b
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-result.c
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-qcdm-result.h"
+#include "result.h"
+#include "result-private.h"
+
+#define TEST_TAG "test"
+
+void
+test_result_string (void *f, void *data)
+{
+ const char *str = "foobarblahblahblah";
+ const char *tmp = NULL;
+ QCDMResult *result;
+
+ result = qcdm_result_new ();
+ qcdm_result_add_string (result, TEST_TAG, str);
+
+ qcdm_result_get_string (result, TEST_TAG, &tmp);
+ g_assert (tmp);
+ g_assert (strcmp (tmp, str) == 0);
+
+ qcdm_result_unref (result);
+}
+
+void
+test_result_uint32 (void *f, void *data)
+{
+ guint32 num = 0xDEADBEEF;
+ guint32 tmp = 0;
+ QCDMResult *result;
+
+ result = qcdm_result_new ();
+ qcdm_result_add_uint32 (result, TEST_TAG, num);
+
+ qcdm_result_get_uint32 (result, TEST_TAG, &tmp);
+ g_assert (tmp == num);
+}
+
+void
+test_result_uint8 (void *f, void *data)
+{
+ guint8 num = 0x1E;
+ guint8 tmp = 0;
+ QCDMResult *result;
+
+ result = qcdm_result_new ();
+ qcdm_result_add_uint8 (result, TEST_TAG, num);
+
+ qcdm_result_get_uint8 (result, TEST_TAG, &tmp);
+ g_assert (tmp == num);
+}
+
diff --git a/libqcdm/tests/test-qcdm-result.h b/libqcdm/tests/test-qcdm-result.h
new file mode 100644
index 0000000..20d6cec
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-result.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEST_QCDM_RESULT_H
+#define TEST_QCDM_RESULT_H
+
+void test_result_string (void *f, void *data);
+void test_result_uint32 (void *f, void *data);
+void test_result_uint8 (void *f, void *data);
+
+#endif /* TEST_QCDM_RESULT_H */
+
diff --git a/libqcdm/tests/test-qcdm-utils.c b/libqcdm/tests/test-qcdm-utils.c
new file mode 100644
index 0000000..27ec8d6
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-utils.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-qcdm-utils.h"
+#include "utils.h"
+
+static const char decap_inbuf[] = {
+ 0x40, 0x03, 0x00, 0x01, 0x00, 0x19, 0xf0, 0x00, 0x16, 0x00, 0x21, 0x00,
+ 0x1c, 0x00, 0xd8, 0x00, 0x3f, 0x00, 0x56, 0x01, 0x3f, 0x00, 0x15, 0x00,
+ 0x1a, 0x00, 0x11, 0x01, 0x3f, 0x00, 0x92, 0x01, 0x3f, 0x00, 0x39, 0x00,
+ 0x3f, 0x00, 0x95, 0x01, 0x3f, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x23, 0x01,
+ 0x3f, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x0b, 0x01, 0x3f, 0x00, 0xae, 0x00,
+ 0x3f, 0x00, 0x02, 0x01, 0x3f, 0x00, 0xa8, 0x00, 0x3f, 0x00, 0x50, 0x01,
+ 0x3f, 0x00, 0xf8, 0x01, 0x3f, 0x00, 0x57, 0x00, 0x3f, 0x00, 0x7d, 0x5e,
+ 0x00, 0x3f, 0x00, 0x93, 0x00, 0x3f, 0x00, 0xbd, 0x00, 0x3f, 0x00, 0x77,
+ 0x01, 0x3f, 0x00, 0xb7, 0x00, 0x3f, 0x00, 0xab, 0x00, 0x3f, 0x00, 0x33,
+ 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
+ 0x13, 0x50, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x00, 0xaa, 0x19, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0xc4, 0x7d, 0x5e,
+ 0x7d, 0x5e, 0x7d, 0x5d, 0x5d, 0x04, 0x58, 0x1b, 0x5b, 0x1b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x65, 0x69, 0x7e
+};
+
+void
+test_utils_decapsulate_buffer (void *f, void *data)
+{
+ gboolean success;
+ char outbuf[512];
+ gsize decap_len = 0;
+ gsize used = 0;
+ gboolean more = FALSE;
+
+ success = dm_decapsulate_buffer (decap_inbuf, sizeof (decap_inbuf),
+ outbuf, sizeof (outbuf),
+ &decap_len, &used, &more);
+ g_assert (success);
+ g_assert (decap_len == 214);
+ g_assert (used == 221);
+ g_assert (more == FALSE);
+}
+
+
+static const char encap_outbuf[] = {
+ 0x4b, 0x05, 0x08, 0x00, 0x01, 0xdd, 0x7e
+};
+
+void
+test_utils_encapsulate_buffer (void *f, void *data)
+{
+ char cmdbuf[10];
+ char outbuf[512];
+ gsize encap_len = 0;
+
+ cmdbuf[0] = 0x4B; /* DIAG_CMD_SUBSYS */
+ cmdbuf[1] = 0x05; /* DIAG_SUBSYS_HDR */
+ cmdbuf[2] = 0x08; /* first byte of DIAG_SUBSYS_HDR_STATE_INFO in LE */
+ cmdbuf[3] = 0x00; /* second byte of DIAG_SUBSYS_HDR_STATE_INFO in LE */
+
+ encap_len = dm_encapsulate_buffer (cmdbuf, 4, sizeof (cmdbuf),
+ &outbuf[0], sizeof (outbuf));
+ g_assert (encap_len == sizeof (encap_outbuf));
+ g_assert (memcmp (outbuf, encap_outbuf, encap_len) == 0);
+}
+
diff --git a/libqcdm/tests/test-qcdm-utils.h b/libqcdm/tests/test-qcdm-utils.h
new file mode 100644
index 0000000..8038666
--- /dev/null
+++ b/libqcdm/tests/test-qcdm-utils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEST_QCDM_UTILS_H
+#define TEST_QCDM_UTILS_H
+
+void test_utils_decapsulate_buffer (void *f, void *data);
+
+void test_utils_encapsulate_buffer (void *f, void *data);
+
+#endif /* TEST_QCDM_UTILS_H */
+
diff --git a/libqcdm/tests/test-qcdm.c b/libqcdm/tests/test-qcdm.c
new file mode 100644
index 0000000..a58780e
--- /dev/null
+++ b/libqcdm/tests/test-qcdm.c
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-qcdm-crc.h"
+#include "test-qcdm-escaping.h"
+#include "test-qcdm-com.h"
+#include "test-qcdm-result.h"
+#include "test-qcdm-utils.h"
+
+typedef struct {
+ gpointer com_data;
+} TestData;
+
+typedef void (*TCFunc)(void);
+
+#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
+
+static TestData *
+test_data_new (const char *port)
+{
+ TestData *d;
+
+ d = g_malloc0 (sizeof (TestData));
+ g_assert (d);
+
+ if (port)
+ d->com_data = test_com_setup (port);
+
+ return d;
+}
+
+static void
+test_data_free (TestData *d)
+{
+ if (d->com_data)
+ test_com_teardown (d->com_data);
+
+ g_free (d);
+}
+
+int main (int argc, char **argv)
+{
+ GTestSuite *suite;
+ TestData *data;
+ int i;
+ const char *port = NULL;
+ gint result;
+
+ g_test_init (&argc, &argv, NULL);
+
+ /* See if we got passed a serial port for live testing */
+ for (i = 0; i < argc; i++) {
+ if (!strcmp (argv[i], "--port")) {
+ /* Make sure there's actually a port in the next arg */
+ g_assert (argc > i + 1);
+ port = argv[++i];
+ }
+ }
+
+ data = test_data_new (port);
+
+ suite = g_test_get_root ();
+
+ g_test_suite_add (suite, TESTCASE (test_crc16_1, NULL));
+ g_test_suite_add (suite, TESTCASE (test_crc16_2, NULL));
+ g_test_suite_add (suite, TESTCASE (test_escape1, NULL));
+ g_test_suite_add (suite, TESTCASE (test_escape2, NULL));
+ g_test_suite_add (suite, TESTCASE (test_escape_unescape, NULL));
+ g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_buffer, NULL));
+ g_test_suite_add (suite, TESTCASE (test_utils_encapsulate_buffer, NULL));
+ g_test_suite_add (suite, TESTCASE (test_result_string, NULL));
+ g_test_suite_add (suite, TESTCASE (test_result_uint32, NULL));
+ g_test_suite_add (suite, TESTCASE (test_result_uint8, NULL));
+
+ /* Live tests */
+ if (port) {
+ g_test_suite_add (suite, TESTCASE (test_com_port_init, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_version_info, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_esn, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_mdn, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_read_roam_pref, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_read_mode_pref, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_status, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_sw_version, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_pilot_sets, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_cm_subsys_state_info, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_hdr_subsys_state_info, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_zte_subsys_status, data->com_data));
+ g_test_suite_add (suite, TESTCASE (test_com_nw_subsys_modem_snapshot_cdma, data->com_data));
+ }
+
+ result = g_test_run ();
+
+ test_data_free (data);
+
+ return result;
+}
+
diff --git a/m4/compiler_warnings.m4 b/m4/compiler_warnings.m4
new file mode 100644
index 0000000..6cea2f7
--- /dev/null
+++ b/m4/compiler_warnings.m4
@@ -0,0 +1,31 @@
+AC_DEFUN([NM_COMPILER_WARNINGS],
+[AC_ARG_ENABLE(more-warnings,
+ AS_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]),
+ set_more_warnings="$enableval",set_more_warnings=yes)
+AC_MSG_CHECKING(for more warnings, including -Werror)
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+ AC_MSG_RESULT(yes)
+ CFLAGS="-Wall -Werror -std=gnu89 $CFLAGS"
+
+ for option in -Wshadow -Wmissing-declarations -Wmissing-prototypes \
+ -Wdeclaration-after-statement -Wstrict-prototypes \
+ -Wfloat-equal -Wno-unused-parameter -Wno-sign-compare \
+ -fno-strict-aliasing; do
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $option"
+ AC_MSG_CHECKING([whether gcc understands $option])
+ AC_TRY_COMPILE([], [],
+ has_option=yes,
+ has_option=no,)
+ if test $has_option = no; then
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ AC_MSG_RESULT($has_option)
+ unset has_option
+ unset SAVE_CFLAGS
+ done
+ unset option
+else
+ AC_MSG_RESULT(no)
+fi
+])
diff --git a/marshallers/mm-marshal.list b/marshallers/mm-marshal.list
index 474d704..c209bbb 100644
--- a/marshallers/mm-marshal.list
+++ b/marshallers/mm-marshal.list
@@ -5,4 +5,5 @@ VOID:UINT,BOOLEAN
VOID:UINT,UINT
VOID:UINT,UINT,UINT
VOID:STRING,BOXED
+VOID:POINTER,UINT
diff --git a/org.freedesktop.ModemManager.conf b/org.freedesktop.ModemManager.conf.nopolkit
index 5d62d7a..2f33161 100644
--- a/org.freedesktop.ModemManager.conf
+++ b/org.freedesktop.ModemManager.conf.nopolkit
@@ -2,6 +2,8 @@
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
+ <!-- This config allows anyone to control ModemManager -->
+
<policy context="default">
<allow send_destination="org.freedesktop.ModemManager"/>
</policy>
@@ -12,3 +14,4 @@
<limit name="max_replies_per_connection">512</limit>
</busconfig>
+
diff --git a/org.freedesktop.ModemManager.conf.polkit b/org.freedesktop.ModemManager.conf.polkit
new file mode 100644
index 0000000..25490e3
--- /dev/null
+++ b/org.freedesktop.ModemManager.conf.polkit
@@ -0,0 +1,154 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy context="default">
+ <deny send_destination="org.freedesktop.ModemManager"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.DBus.Introspectable"/>
+
+ <!-- Methods listed here are explicitly allowed or PolicyKit protected.
+ The rest are restricted to root for security.
+ -->
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager"
+ send_member="EnumerateDevices"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.DBus.Properties"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem"
+ send_member="GetInfo"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Cdma"
+ send_member="GetSignalQuality"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Cdma"
+ send_member="GetServingSystem"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Cdma"
+ send_member="GetRegistrationState"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Cdma"
+ send_member="GetEsn"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Network"
+ send_member="GetSignalQuality"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Network"
+ send_member="GetBand"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Network"
+ send_member="GetNetworkMode"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Network"
+ send_member="GetRegistrationInfo"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Network"
+ send_member="Scan"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Card"
+ send_member="GetImei"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Card"
+ send_member="GetImsi"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Card"
+ send_member="SendPuk"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Card"
+ send_member="SendPin"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Card"
+ send_member="EnablePin"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Card"
+ send_member="ChangePin"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Contacts"
+ send_member="Add"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Contacts"
+ send_member="Delete"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Contacts"
+ send_member="Get"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Contacts"
+ send_member="List"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Contacts"
+ send_member="Find"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.Contacts"
+ send_member="GetCount"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="Delete"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="Get"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="List"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="Save"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="Send"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="SendFromStorage"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="SetIndication"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="GetSmsc"/>
+
+ <allow send_destination="org.freedesktop.ModemManager"
+ send_interface="org.freedesktop.ModemManager.Modem.Gsm.SMS"
+ send_member="SetSmsc"/>
+ </policy>
+
+ <policy user="root">
+ <allow own="org.freedesktop.ModemManager"/>
+ <allow send_destination="org.freedesktop.ModemManager"/>
+ </policy>
+
+ <limit name="max_replies_per_connection">512</limit>
+</busconfig>
+
diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules
index 71dc6b8..8804036 100644
--- a/plugins/77-mm-ericsson-mbm.rules
+++ b/plugins/77-mm-ericsson-mbm.rules
@@ -20,9 +20,15 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C3607w
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{ID_MM_ERICSSON_MBM}="1"
+# Ericsson C3607w v2
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1"
+
# Sony-Ericsson MD300
ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1"
+# Sony-Ericsson MD400
+ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0e1", ENV{ID_MM_ERICSSON_MBM}="1"
+
# Dell 5530 HSDPA
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1"
@@ -30,6 +36,10 @@ ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1"
+# Dell F3307
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{ID_MM_ERICSSON_MBM}="1"
+
# Toshiba
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1"
diff --git a/plugins/77-mm-longcheer-port-types.rules b/plugins/77-mm-longcheer-port-types.rules
index 7317df7..ce0134b 100644
--- a/plugins/77-mm-longcheer-port-types.rules
+++ b/plugins/77-mm-longcheer-port-types.rules
@@ -6,6 +6,11 @@
# Alcatel One Touch X030
# MobiData MBD-200HU
# ST Mobile Connect HSUPA USB Modem
+#
+# Most of these values were scraped from various Longcheer-based Windows
+# driver .inf files. cmmdm.inf lists the actual data (ie PPP) ports, while
+# cmser.inf lists the aux ports that may be either AT-capable or not but
+# cannot be used for PPP.
ACTION!="add|change", GOTO="mm_longcheer_port_types_end"
@@ -18,6 +23,14 @@ GOTO="mm_longcheer_port_types_end"
LABEL="mm_longcheer_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="3197", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="6000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
ATTRS{idProduct}=="6060", ENV{ID_MM_LONGCHEER_TAGGED}="1"
@@ -27,6 +40,127 @@ ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE
ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
ATTRS{idProduct}=="6061", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7001", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7002", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7101", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="7102", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="8000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="8001", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="8002", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+# ChinaBird PL68
+ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9001", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9002", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9003", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9004", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9005", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9010", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9012", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9020", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9022", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+# Zoom products
+ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9602", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9603", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9604", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9605", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9606", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
+ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9607", ENV{ID_MM_LONGCHEER_TAGGED}="1"
+
GOTO="mm_longcheer_port_types_end"
diff --git a/plugins/77-mm-simtech-port-types.rules b/plugins/77-mm-simtech-port-types.rules
new file mode 100644
index 0000000..9ec047c
--- /dev/null
+++ b/plugins/77-mm-simtech-port-types.rules
@@ -0,0 +1,29 @@
+# do not edit this file, it will be overwritten on update
+
+# Simtech makes modules that other companies rebrand, like:
+#
+# A-LINK 3GU
+#
+# Most of these values were scraped from various SimTech-based Windows
+# driver .inf files. *mdm.inf lists the main command ports, while
+# *ser.inf lists the aux ports that may be used for PPP.
+
+
+ACTION!="add|change", GOTO="mm_simtech_port_types_end"
+SUBSYSTEM!="tty", GOTO="mm_simtech_port_types_end"
+
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e0e", GOTO="mm_alink_vendorcheck"
+GOTO="mm_simtech_port_types_end"
+
+LABEL="mm_alink_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1"
+ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1"
+ATTRS{idProduct}=="9200", ENV{ID_MM_SIMTECH_TAGGED}="1"
+
+GOTO="mm_simtech_port_types_end"
+
+
+LABEL="mm_simtech_port_types_end"
+
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index a361358..8192653 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -11,7 +11,8 @@ pkglib_LTLIBRARIES = \
libmm-plugin-zte.la \
libmm-plugin-mbm.la \
libmm-plugin-longcheer.la \
- libmm-plugin-anydata.la
+ libmm-plugin-anydata.la \
+ libmm-plugin-simtech.la
# Generic
@@ -169,7 +170,9 @@ libmm_plugin_novatel_la_SOURCES = \
mm-plugin-novatel.c \
mm-plugin-novatel.h \
mm-modem-novatel-gsm.c \
- mm-modem-novatel-gsm.h
+ mm-modem-novatel-gsm.h \
+ mm-modem-novatel-cdma.c \
+ mm-modem-novatel-cdma.h
libmm_plugin_novatel_la_CPPFLAGS = \
$(MM_CFLAGS) \
@@ -221,7 +224,9 @@ libmm_plugin_zte_la_LDFLAGS = \
libmm_plugin_longcheer_la_SOURCES = \
mm-plugin-longcheer.c \
- mm-plugin-longcheer.h
+ mm-plugin-longcheer.h \
+ mm-modem-longcheer-gsm.c \
+ mm-modem-longcheer-gsm.h
libmm_plugin_longcheer_la_CPPFLAGS = \
$(MM_CFLAGS) \
@@ -251,12 +256,31 @@ libmm_plugin_anydata_la_LDFLAGS = \
-module \
-avoid-version
+# SimTech
+
+libmm_plugin_simtech_la_SOURCES = \
+ mm-plugin-simtech.c \
+ mm-plugin-simtech.h \
+ mm-modem-simtech-gsm.c \
+ mm-modem-simtech-gsm.h
+
+libmm_plugin_simtech_la_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ $(GUDEV_CFLAGS) \
+ -I$(top_srcdir)/src
+
+libmm_plugin_simtech_la_LDFLAGS = \
+ $(GUDEV_LDFLAGS) \
+ -module \
+ -avoid-version
+
udevrulesdir = $(UDEV_BASE_DIR)/rules.d
udevrules_DATA = \
77-mm-ericsson-mbm.rules \
77-mm-zte-port-types.rules \
- 77-mm-longcheer-port-types.rules
+ 77-mm-longcheer-port-types.rules \
+ 77-mm-simtech-port-types.rules
BUILT_SOURCES = \
mm-modem-gsm-hso-glue.h
@@ -264,5 +288,6 @@ BUILT_SOURCES = \
CLEANFILES = $(BUILT_SOURCES)
EXTRA_DIST = \
- $(udevrules_DATA)
+ $(udevrules_DATA) \
+ mm-modem-option-utils.c
diff --git a/plugins/mm-modem-anydata-cdma.c b/plugins/mm-modem-anydata-cdma.c
index f6528ec..c7cca46 100644
--- a/plugins/mm-modem-anydata-cdma.c
+++ b/plugins/mm-modem-anydata-cdma.c
@@ -112,7 +112,7 @@ int_from_match_item (GMatchInfo *match_info, guint32 num, gint *val)
}
static void
-evdo_state_done (MMSerialPort *port,
+evdo_state_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -123,15 +123,8 @@ evdo_state_done (MMSerialPort *port,
GRegex *r;
GMatchInfo *match_info;
- info->error = mm_modem_check_removed (info->modem, error);
- if (info->error) {
- if (info->modem) {
- /* If HSTATE returned an error, assume the device is not EVDO capable
- * or EVDO is not registered.
- */
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
- }
-
+ if (error) {
+ /* Leave superclass' reg state alone if AT*HSTATE isn't supported */
mm_callback_info_schedule (info);
return;
}
@@ -143,13 +136,8 @@ evdo_state_done (MMSerialPort *port,
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
if (!r) {
/* Parse error; warn about it and assume EVDO is not available */
- g_warning ("AnyData(%s): failed to create EVDO state regex: (%d) %s",
- __func__,
- error ? error->code : -1,
- error && error->message ? error->message : "(unknown)");
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
- mm_callback_info_schedule (info);
- return;
+ g_warning ("AnyDATA(%s): *HSTATE parse regex creation failed.", __func__);
+ goto done;
}
g_regex_match (r, reply, 0, &match_info);
@@ -185,13 +173,13 @@ evdo_state_done (MMSerialPort *port,
}
}
+done:
mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state);
-
mm_callback_info_schedule (info);
}
static void
-state_done (MMSerialPort *port,
+state_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -202,17 +190,8 @@ state_done (MMSerialPort *port,
GRegex *r;
GMatchInfo *match_info;
- info->error = mm_modem_check_removed (info->modem, error);
- if (info->error) {
- if (info->modem) {
- /* Assume if we got this far, we're registered even if an error
- * occurred. We're not sure if all AnyData CDMA modems support
- * the *STATE and *HSTATE commands.
- */
- 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);
- }
-
+ if (error) {
+ /* Leave superclass' reg state alone if AT*STATE isn't supported */
mm_callback_info_schedule (info);
return;
}
@@ -223,9 +202,7 @@ state_done (MMSerialPort *port,
r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,.*",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
if (!r) {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse sysinfo results (regex creation failed).");
+ g_warning ("AnyDATA(%s): *STATE parse regex creation failed.", __func__);
mm_callback_info_schedule (info);
return;
}
@@ -254,7 +231,7 @@ state_done (MMSerialPort *port,
reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
default:
- g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val);
+ g_warning ("ANYDATA: unknown *STATE (%d); assuming no service.", val);
/* fall through */
case 0: /* NO SERVICE */
break;
@@ -265,35 +242,28 @@ state_done (MMSerialPort *port,
mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state);
/* Try for EVDO state too */
- mm_serial_port_queue_command (port, "*HSTATE?", 3, evdo_state_done, info);
+ mm_at_serial_port_queue_command (port, "*HSTATE?", 3, evdo_state_done, info);
}
static void
query_registration_state (MMGenericCdma *cdma,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary, *secondary, *port;
-
- port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY);
- secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY);
-
- info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data);
+ MMAtSerialPort *port;
- if (mm_port_get_connected (MM_PORT (primary))) {
- if (!secondary) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get query registration state while connected");
- mm_callback_info_schedule (info);
- return;
- }
+ info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data);
- /* Use secondary port if primary is connected */
- port = secondary;
+ port = mm_generic_cdma_get_best_at_port (cdma, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
- mm_serial_port_queue_command (port, "*STATE?", 3, state_done, info);
+ mm_at_serial_port_queue_command (port, "*STATE?", 3, state_done, info);
}
/*****************************************************************************/
@@ -310,22 +280,22 @@ grab_port (MMModem *modem,
GRegex *regex;
port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error);
- if (port && MM_IS_SERIAL_PORT (port)) {
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
/* Data state notifications */
/* Data call has connected */
regex = g_regex_new ("\\r\\n\\*ACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Data call disconnected */
regex = g_regex_new ("\\r\\n\\*INACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Modem is now dormant */
regex = g_regex_new ("\\r\\n\\*DORMANT:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Abnomral state notifications
@@ -336,17 +306,17 @@ grab_port (MMModem *modem,
/* Network acquisition fail */
regex = g_regex_new ("\\r\\n\\*OFFLINE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Registration fail */
regex = g_regex_new ("\\r\\n\\*REGREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Authentication fail */
regex = g_regex_new ("\\r\\n\\*AUTHREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
}
diff --git a/plugins/mm-modem-gobi-gsm.c b/plugins/mm-modem-gobi-gsm.c
index 7ea9f8f..3b9e9ec 100644
--- a/plugins/mm-modem-gobi-gsm.c
+++ b/plugins/mm-modem-gobi-gsm.c
@@ -23,6 +23,7 @@
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-modem-gsm-card.h"
+#include "mm-at-serial-port.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -51,7 +52,7 @@ mm_modem_gobi_gsm_new (const char *device,
/*****************************************************************************/
static void
-get_string_done (MMSerialPort *port,
+get_string_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -75,13 +76,16 @@ get_imsi (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
{
- MMSerialPort *primary;
+ MMAtSerialPort *port;
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command_cached (primary, "+CIMI", 3, get_string_done, info);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (port)
+ mm_at_serial_port_queue_command_cached (port, "+CIMI", 3, get_string_done, info);
+ else
+ mm_callback_info_schedule (info);
}
static void
diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c
index f1295e2..1fd4633 100644
--- a/plugins/mm-modem-hso.c
+++ b/plugins/mm-modem-hso.c
@@ -63,6 +63,9 @@ typedef struct {
MMCallbackInfo *connect_pending_data;
guint connect_pending_id;
+ char *username;
+ char *password;
+
guint32 auth_idx;
} MMModemHsoPrivate;
@@ -85,62 +88,122 @@ mm_modem_hso_new (const char *device,
NULL));
}
-#define IGNORE_ERRORS_TAG "ignore-errors"
+#include "mm-modem-option-utils.c"
+
+/*****************************************************************************/
+
+static gint
+hso_get_cid (MMModemHso *self)
+{
+ gint cid;
+
+ cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self));
+ if (cid < 0) {
+ g_warn_if_fail (cid >= 0);
+ cid = 0;
+ }
+
+ return cid;
+}
static void
-hso_call_control_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+auth_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemHso *self = MM_MODEM_HSO (info->modem);
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
- if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG))
- info->error = g_error_copy (error);
+ if (error) {
+ priv->auth_idx++;
+ if (auth_commands[priv->auth_idx]) {
+ /* Try the next auth command */
+ _internal_hso_modem_authenticate (self, info);
+ return;
+ } else
+ info->error = g_error_copy (error);
+ }
+ /* Reset to 0 so something gets tried the next connection */
+ priv->auth_idx = 0;
mm_callback_info_schedule (info);
}
-static guint32
-hso_get_cid (MMModemHso *self)
+static void
+_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info)
{
- guint32 cid;
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
+ MMAtSerialPort *primary;
+ gint cid;
+ char *command;
- cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self));
- if (cid == 0)
- cid = 1;
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
- return cid;
+ cid = hso_get_cid (self);
+ g_warn_if_fail (cid >= 0);
+
+ /* Both user and password are required; otherwise firmware returns an error */
+ if (!priv->username || !priv->password)
+ command = g_strdup_printf ("%s=%d,0", auth_commands[priv->auth_idx], cid);
+ else {
+ command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"",
+ auth_commands[priv->auth_idx],
+ cid,
+ priv->password ? priv->password : "",
+ priv->username ? priv->username : "");
+
+ }
+
+ mm_at_serial_port_queue_command (primary, command, 3, auth_done, info);
+ g_free (command);
}
-static void
-hso_call_control (MMModemHso *self,
- gboolean activate,
- gboolean ignore_errors,
- MMModemFn callback,
- gpointer user_data)
+void
+mm_hso_modem_authenticate (MMModemHso *self,
+ const char *username,
+ const char *password,
+ MMModemFn callback,
+ gpointer user_data)
{
+ MMModemHsoPrivate *priv;
MMCallbackInfo *info;
- char *command;
- MMSerialPort *primary;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_HSO (self));
+ g_return_if_fail (callback != NULL);
info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
- mm_callback_info_set_data (info, IGNORE_ERRORS_TAG, GUINT_TO_POINTER (ignore_errors), NULL);
- command = g_strdup_printf ("AT_OWANCALL=%d,%d,1", hso_get_cid (self), activate ? 1 : 0);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, command, 3, hso_call_control_done, info);
- g_free (command);
+ priv = MM_MODEM_HSO_GET_PRIVATE (self);
+
+ g_free (priv->username);
+ priv->username = (username && strlen (username)) ? g_strdup (username) : NULL;
+
+ g_free (priv->password);
+ priv->password = (password && strlen (password)) ? g_strdup (password) : NULL;
+
+ _internal_hso_modem_authenticate (self, info);
}
+/*****************************************************************************/
+
static void
connect_pending_done (MMModemHso *self)
{
MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
+ GError *error = NULL;
if (priv->connect_pending_data) {
- mm_callback_info_schedule (priv->connect_pending_data);
+ if (priv->connect_pending_data->error) {
+ error = priv->connect_pending_data->error;
+ priv->connect_pending_data->error = NULL;
+ }
+
+ /* Complete the connect */
+ mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), error, priv->connect_pending_data);
priv->connect_pending_data = NULL;
}
@@ -150,177 +213,175 @@ connect_pending_done (MMModemHso *self)
}
}
-static gboolean
-hso_connect_timed_out (gpointer data)
+static void
+connection_enabled (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
{
- MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (data);
+ MMModemHso *self = MM_MODEM_HSO (user_data);
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
+ char *str;
+
+ str = g_match_info_fetch (match_info, 2);
+ if (str[0] == '1')
+ connect_pending_done (self);
+ else if (str[0] == '3') {
+ MMCallbackInfo *info = priv->connect_pending_data;
- priv->connect_pending_data->error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_RESPONSE_TIMEOUT,
- "Connection timed out");
- connect_pending_done (MM_MODEM_HSO (data));
+ if (info) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Call setup failed");
+ }
- return FALSE;
+ connect_pending_done (self);
+ } else if (str[0] == '0') {
+ /* FIXME: disconnected. do something when we have modem status signals */
+ }
+
+ g_free (str);
}
+/*****************************************************************************/
+
+#define IGNORE_ERRORS_TAG "ignore-errors"
+
static void
-hso_enabled (MMModem *modem,
- GError *error,
- gpointer user_data)
+hso_call_control_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error) {
+ if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG))
info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- } else {
- MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (modem);
- GSource *source;
- source = g_timeout_source_new_seconds (30);
- g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (hso_connect_timed_out), G_OBJECT (modem)));
- g_source_attach (source, NULL);
- priv->connect_pending_data = info;
- priv->connect_pending_id = g_source_get_id (source);
- g_source_unref (source);
- }
+ mm_callback_info_schedule (info);
}
static void
-clear_old_context (MMModem *modem,
- GError *error,
- gpointer user_data)
+hso_call_control (MMModemHso *self,
+ gboolean activate,
+ gboolean ignore_errors,
+ MMModemFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMCallbackInfo *info;
+ char *command;
+ MMAtSerialPort *primary;
- if (error) {
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- } else {
- /* Success, activate the PDP context and start the data session */
- hso_call_control (MM_MODEM_HSO (modem), TRUE, FALSE, hso_enabled, info);
- }
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ mm_callback_info_set_data (info, IGNORE_ERRORS_TAG, GUINT_TO_POINTER (ignore_errors), NULL);
+
+ command = g_strdup_printf ("AT_OWANCALL=%d,%d,1", hso_get_cid (self), activate ? 1 : 0);
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
+ mm_at_serial_port_queue_command (primary, command, 3, hso_call_control_done, info);
+ g_free (command);
}
static void
-auth_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+timeout_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMModemHso *self = MM_MODEM_HSO (info->modem);
- MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
-
- if (error) {
- priv->auth_idx++;
- if (auth_commands[priv->auth_idx]) {
- /* Try the next auth command */
- _internal_hso_modem_authenticate (self, info);
- } else {
- /* Reset to 0 so that something gets tried for the next connection */
- priv->auth_idx = 0;
-
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- }
- } else {
- priv->auth_idx = 0;
-
- /* success, kill any existing connections first */
- hso_call_control (self, FALSE, TRUE, clear_old_context, info);
- }
+ if (modem)
+ connect_pending_done (MM_MODEM_HSO (modem));
}
-static void
-_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info)
+static gboolean
+hso_connect_timed_out (gpointer data)
{
+ MMModemHso *self = MM_MODEM_HSO (data);
MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
- MMSerialPort *primary;
- guint32 cid;
- char *command;
- const char *username, *password;
-
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
-
- cid = hso_get_cid (self);
-
- username = mm_callback_info_get_data (info, "username");
- password = mm_callback_info_get_data (info, "password");
+ MMCallbackInfo *info = priv->connect_pending_data;
- if (!username && !password)
- command = g_strdup_printf ("%s=%d,0", auth_commands[priv->auth_idx], cid);
- else {
- command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"",
- auth_commands[priv->auth_idx],
- cid,
- password ? password : "",
- username ? username : "");
+ priv->connect_pending_id = 0;
+ if (info) {
+ info->error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
+ "Connection timed out");
}
- mm_serial_port_queue_command (primary, command, 3, auth_done, info);
- g_free (command);
+ hso_call_control (self, FALSE, TRUE, timeout_done, self);
+ return FALSE;
}
-void
-mm_hso_modem_authenticate (MMModemHso *self,
- const char *username,
- const char *password,
- MMModemFn callback,
- gpointer user_data)
+static void
+hso_enabled (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info;
-
- g_return_if_fail (MM_IS_MODEM_HSO (self));
- g_return_if_fail (callback != NULL);
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GError *tmp_error;
- info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
- if (username)
- mm_callback_info_set_data (info, "username", g_strdup (username), g_free);
- if (password)
- mm_callback_info_set_data (info, "password", g_strdup (password), g_free);
+ tmp_error = mm_modem_check_removed (modem, error);
+ if (tmp_error) {
+ mm_generic_gsm_connect_complete (MM_GENERIC_GSM (modem), tmp_error, info);
+ g_clear_error (&tmp_error);
+ } else {
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (modem);
- _internal_hso_modem_authenticate (self, info);
+ priv->connect_pending_data = info;
+ priv->connect_pending_id = g_timeout_add_seconds (30, hso_connect_timed_out, modem);
+ }
}
-/*****************************************************************************/
-
static void
-enable_done (MMModem *modem, GError *error, gpointer user_data)
+old_context_clear_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GError *tmp_error;
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info);
+ tmp_error = mm_modem_check_removed (modem, error);
+ if (tmp_error) {
+ mm_generic_gsm_connect_complete (MM_GENERIC_GSM (modem), tmp_error, info);
+ g_clear_error (&tmp_error);
+ } else {
+ /* Success, activate the PDP context and start the data session */
+ hso_call_control (MM_MODEM_HSO (modem), TRUE, FALSE, hso_enabled, info);
+ }
}
static void
-parent_enable_done (MMModem *modem, GError *error, gpointer user_data)
+connect_auth_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ GError *tmp_error;
- if (error) {
- mm_generic_gsm_enable_complete (self, error, info);
- return;
+ tmp_error = mm_modem_check_removed (modem, error);
+ if (tmp_error) {
+ mm_generic_gsm_connect_complete (MM_GENERIC_GSM (modem), tmp_error, info);
+ g_clear_error (&tmp_error);
+ } else {
+ /* Now connect; kill any existing connections first */
+ hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, old_context_clear_done, info);
}
-
- /* HSO needs manual PIN checking */
- mm_generic_gsm_check_pin (self, enable_done, info);
}
static void
-enable (MMModem *modem, MMModemFn callback, gpointer user_data)
+do_connect (MMModem *modem,
+ const char *number,
+ MMModemFn callback,
+ gpointer user_data)
{
- MMModem *parent_modem_iface;
- MMCallbackInfo *info;
+ MMModemHso *self = MM_MODEM_HSO (modem);
+ MMCallbackInfo *auth_info, *connect_info;
- info = mm_callback_info_new (modem, callback, user_data);
- parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem));
- parent_modem_iface->enable (info->modem, parent_enable_done, info);
+ mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE);
+
+ connect_info = mm_callback_info_new (modem, callback, user_data);
+ auth_info = mm_callback_info_new (modem, connect_auth_done, connect_info);
+ _internal_hso_modem_authenticate (self, auth_info);
}
+/*****************************************************************************/
+
static void
parent_disable_done (MMModem *modem, GError *error, gpointer user_data)
{
@@ -345,32 +406,56 @@ disable_done (MMModem *modem,
}
static void
-disable (MMModem *modem,
- MMModemFn callback,
- gpointer user_data)
+unsolicited_disable_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info;
+ MMCallbackInfo *info = user_data;
- mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ /* Handle modem removal, but ignore other errors */
+ if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED))
+ info->error = g_error_copy (error);
+ else if (!modem) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_REMOVED,
+ "The modem was removed.");
+ }
- info = mm_callback_info_new (modem, callback, user_data);
+ if (info->error) {
+ mm_callback_info_schedule (info);
+ return;
+ }
- /* Kill any existing connection */
- hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info);
+ /* Otherwise, kill any existing connection */
+ if (mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)) >= 0)
+ hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info);
+ else
+ disable_done (modem, NULL, info);
}
static void
-do_connect (MMModem *modem,
- const char *number,
- MMModemFn callback,
- gpointer user_data)
+disable (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
{
+ MMModemHso *self = MM_MODEM_HSO (modem);
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
MMCallbackInfo *info;
+ mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+
+ g_free (priv->username);
+ priv->username = NULL;
+ g_free (priv->password);
+ priv->password = NULL;
+
info = mm_callback_info_new (modem, callback, user_data);
- mm_callback_info_schedule (info);
+
+ /* Turn off unsolicited messages so they don't pile up in the modem */
+ option_change_unsolicited_messages (MM_GENERIC_GSM (modem), FALSE, unsolicited_disable_done, info);
}
+/*****************************************************************************/
static void
free_dns_array (gpointer data)
@@ -390,7 +475,7 @@ ip4_config_invoke (MMCallbackInfo *info)
}
static void
-get_ip4_config_done (MMSerialPort *port,
+get_ip4_config_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -400,7 +485,7 @@ get_ip4_config_done (MMSerialPort *port,
GArray *dns_array;
int i;
guint32 tmp;
- guint cid;
+ gint cid;
if (error) {
info->error = g_error_copy (error);
@@ -421,7 +506,7 @@ get_ip4_config_done (MMSerialPort *port,
errno = 0;
num = strtol (*iter, NULL, 10);
- if (errno != 0 || num < 0 || (guint) num != cid) {
+ if (errno != 0 || num < 0 || (gint) num != cid) {
info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Unknown CID in OWANDATA response ("
"got %d, expected %d)", (guint) num, cid);
@@ -453,28 +538,61 @@ get_ip4_config (MMModem *modem,
{
MMCallbackInfo *info;
char *command;
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
info = mm_callback_info_new_full (modem, ip4_config_invoke, G_CALLBACK (callback), user_data);
command = g_strdup_printf ("AT_OWANDATA=%d", hso_get_cid (MM_MODEM_HSO (modem)));
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
g_assert (primary);
- mm_serial_port_queue_command (primary, command, 3, get_ip4_config_done, info);
+ mm_at_serial_port_queue_command (primary, command, 3, get_ip4_config_done, info);
g_free (command);
}
+/*****************************************************************************/
+
static void
-disconnect (MMModem *modem,
- MMModemFn callback,
- gpointer user_data)
+disconnect_owancall_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ mm_callback_info_schedule ((MMCallbackInfo *) user_data);
+}
+
+static void
+do_disconnect (MMGenericGsm *gsm,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
+ char *command;
- info = mm_callback_info_new (modem, callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY);
g_assert (primary);
- mm_serial_port_queue_command (primary, "AT_OWANCALL=1,0,0", 3, NULL, info);
+
+ command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid);
+ mm_at_serial_port_queue_command (primary, command, 3, disconnect_owancall_done, info);
+ g_free (command);
+}
+
+/*****************************************************************************/
+
+static void
+real_do_enable_power_up_done (MMGenericGsm *gsm,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ /* Enable Option unsolicited messages */
+ if (gsm && !error)
+ option_change_unsolicited_messages (gsm, TRUE, NULL, NULL);
+
+ /* Chain up to parent */
+ MM_GENERIC_GSM_CLASS (mm_modem_hso_parent_class)->do_enable_power_up_done (gsm, response, error, info);
}
/*****************************************************************************/
@@ -507,47 +625,11 @@ impl_hso_authenticate (MMModemHso *self,
mm_hso_modem_authenticate (self, username, password, impl_hso_auth_done, context);
}
-static void
-connection_enabled (MMSerialPort *port,
- GMatchInfo *info,
- gpointer user_data)
-{
- MMModemHso *self = MM_MODEM_HSO (user_data);
- MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
- char *str;
-
- str = g_match_info_fetch (info, 2);
- if (str[0] == '1')
- connect_pending_done (self);
- else if (str[0] == '3') {
- MMCallbackInfo *cb_info = priv->connect_pending_data;
-
- if (cb_info)
- cb_info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Call setup failed");
-
- connect_pending_done (self);
- } else if (str[0] == '0')
- /* FIXME: disconnected. do something when we have modem status signals */
- ;
-
- g_free (str);
-}
-
/*****************************************************************************/
-/* MMModemSimple interface */
-
-typedef enum {
- SIMPLE_STATE_BEGIN = 0,
- SIMPLE_STATE_PARENT_CONNECT,
- SIMPLE_STATE_AUTHENTICATE,
- SIMPLE_STATE_DONE
-} SimpleState;
static const char *
-simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+hso_simple_get_string_property (GHashTable *properties, const char *name, GError **error)
{
- GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
GValue *value;
value = (GValue *) g_hash_table_lookup (properties, name);
@@ -565,60 +647,47 @@ simple_get_string_property (MMCallbackInfo *info, const char *name, GError **err
}
static void
-simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
+simple_connect (MMModemSimple *simple,
+ GHashTable *properties,
+ MMModemFn callback,
+ gpointer user_data)
{
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (simple);
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemSimple *parent_iface;
- const char *username;
- const char *password;
- GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
- SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
-
- if (error) {
- info->error = g_error_copy (error);
- goto out;
- }
- switch (state) {
- case SIMPLE_STATE_BEGIN:
- state = SIMPLE_STATE_PARENT_CONNECT;
- parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (modem));
- parent_iface->connect (MM_MODEM_SIMPLE (modem), properties, simple_state_machine, info);
- break;
- case SIMPLE_STATE_PARENT_CONNECT:
- state = SIMPLE_STATE_AUTHENTICATE;
- username = simple_get_string_property (info, "username", &info->error);
- password = simple_get_string_property (info, "password", &info->error);
- mm_hso_modem_authenticate (MM_MODEM_HSO (modem), username, password, simple_state_machine, info);
- break;
- case SIMPLE_STATE_AUTHENTICATE:
- state = SIMPLE_STATE_DONE;
- break;
- default:
- break;
- }
+ priv->username = g_strdup (hso_simple_get_string_property (properties, "username", NULL));
+ priv->password = g_strdup (hso_simple_get_string_property (properties, "password", NULL));
- out:
- if (info->error || state == SIMPLE_STATE_DONE)
- mm_callback_info_schedule (info);
- else
- mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL);
+ parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple));
+ parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info);
}
+/*****************************************************************************/
+
static void
-simple_connect (MMModemSimple *simple,
- GHashTable *properties,
- MMModemFn callback,
- gpointer user_data)
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info;
+ option_get_allowed_mode (gsm, callback, user_data);
+}
- info = mm_callback_info_new (MM_MODEM (simple), callback, user_data);
- mm_callback_info_set_data (info, "simple-connect-properties",
- g_hash_table_ref (properties),
- (GDestroyNotify) g_hash_table_unref);
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ option_set_allowed_mode (gsm, mode, callback, user_data);
+}
- simple_state_machine (MM_MODEM (simple), NULL, info);
+static void
+get_access_technology (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ option_get_access_technology (gsm, callback, user_data);
}
/*****************************************************************************/
@@ -676,17 +745,20 @@ grab_port (MMModem *modem,
if (!port)
goto out;
- if (MM_IS_SERIAL_PORT (port)) {
+ if (MM_IS_AT_SERIAL_PORT (port)) {
g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 10000, NULL);
if (ptype == MM_PORT_TYPE_PRIMARY) {
GRegex *regex;
- mm_generic_gsm_set_unsolicited_registration (gsm, TRUE);
-
regex = g_regex_new ("_OWANCALL: (\\d),\\s*(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, connection_enabled, modem, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, connection_enabled, modem, NULL);
+ g_regex_unref (regex);
+
+ regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
}
+ option_register_unsolicted_handlers (gsm, MM_AT_SERIAL_PORT (port));
}
out:
@@ -712,19 +784,23 @@ modem_simple_init (MMModemSimple *class)
static void
modem_init (MMModem *modem_class)
{
- modem_class->enable = enable;
modem_class->disable = disable;
modem_class->connect = do_connect;
modem_class->get_ip4_config = get_ip4_config;
- modem_class->disconnect = disconnect;
modem_class->grab_port = grab_port;
}
static void
finalize (GObject *object)
{
+ MMModemHso *self = MM_MODEM_HSO (object);
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self);
+
/* Clear the pending connection if necessary */
- connect_pending_done (MM_MODEM_HSO (object));
+ connect_pending_done (self);
+
+ g_free (priv->username);
+ g_free (priv->password);
G_OBJECT_CLASS (mm_modem_hso_parent_class)->finalize (object);
}
@@ -733,11 +809,17 @@ static void
mm_modem_hso_class_init (MMModemHsoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
mm_modem_hso_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (object_class, sizeof (MMModemHsoPrivate));
/* Virtual methods */
object_class->finalize = finalize;
+ gsm_class->do_disconnect = do_disconnect;
+ gsm_class->do_enable_power_up_done = real_do_enable_power_up_done;
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c
index 3b63a48..523578f 100644
--- a/plugins/mm-modem-huawei-cdma.c
+++ b/plugins/mm-modem-huawei-cdma.c
@@ -41,16 +41,26 @@ mm_modem_huawei_cdma_new (const char *device,
gboolean evdo_rev0,
gboolean evdo_revA)
{
+ gboolean try_css = TRUE;
+
g_return_val_if_fail (device != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (plugin != NULL, NULL);
+ /* Don't use AT+CSS on EVDO-capable hardware for determining registration
+ * status, because often the device will have only an EVDO connection and
+ * AT+CSS won't necessarily report EVDO registration status, only 1X.
+ */
+ if (evdo_rev0 || evdo_revA)
+ try_css = FALSE;
+
return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI_CDMA,
MM_MODEM_MASTER_DEVICE, device,
MM_MODEM_DRIVER, driver,
MM_MODEM_PLUGIN, plugin,
MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0,
MM_GENERIC_CDMA_EVDO_REVA, evdo_revA,
+ MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, try_css,
NULL));
}
@@ -72,7 +82,7 @@ parse_quality (const char *str, const char *detail)
}
static void
-handle_1x_quality_change (MMSerialPort *port,
+handle_1x_quality_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
@@ -89,9 +99,9 @@ handle_1x_quality_change (MMSerialPort *port,
}
static void
-handle_evdo_quality_change (MMSerialPort *port,
- GMatchInfo *match_info,
- gpointer user_data)
+handle_evdo_quality_change (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
{
MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data);
char *str;
@@ -142,7 +152,7 @@ uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val)
}
static void
-sysinfo_done (MMSerialPort *port,
+sysinfo_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -151,12 +161,10 @@ sysinfo_done (MMSerialPort *port,
GRegex *r;
GMatchInfo *match_info;
const char *reply;
- gboolean success = FALSE;
if (error) {
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- return;
+ /* Leave superclass' reg state alone if AT^SYSINFO isn't supported */
+ goto done;
}
reply = strip_response (response->str, "^SYSINFO:");
@@ -165,9 +173,7 @@ sysinfo_done (MMSerialPort *port,
r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
if (!r) {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse sysinfo results (regex creation failed).");
+ g_warning ("Huawei(%s): ^SYSINFO parse regex creation failed.", __func__);
goto done;
}
@@ -207,48 +213,35 @@ sysinfo_done (MMSerialPort *port,
/* Say we're registered to something even though sysmode parsing failed */
mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state);
}
- success = TRUE;
- }
+ } else
+ g_warning ("Huawei(%s): failed to parse ^SYSINFO response.", __func__);
-done:
g_match_info_free (match_info);
g_regex_unref (r);
-
- if (!success && !info->error) {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse sysinfo results.");
- }
+done:
mm_callback_info_schedule (info);
}
static void
query_registration_state (MMGenericCdma *cdma,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary, *secondary, *port;
-
- port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY);
- secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY);
+ MMAtSerialPort *port;
- info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data);
+ info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data);
- if (mm_port_get_connected (MM_PORT (primary))) {
- if (!secondary) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get query registration state while connected");
- mm_callback_info_schedule (info);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = secondary;
+ port = mm_generic_cdma_get_best_at_port (cdma, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
- mm_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info);
+ mm_at_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info);
}
/*****************************************************************************/
@@ -265,14 +258,14 @@ grab_port (MMModem *modem,
GRegex *regex;
port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error);
- if (port && MM_IS_SERIAL_PORT (port)) {
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
gboolean evdo0 = FALSE, evdoA = FALSE;
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
/* 1x signal level */
regex = g_regex_new ("\\r\\n\\^RSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_1x_quality_change, modem, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_1x_quality_change, modem, NULL);
g_regex_unref (regex);
g_object_get (G_OBJECT (modem),
@@ -283,7 +276,7 @@ grab_port (MMModem *modem,
if (evdo0 || evdoA) {
/* EVDO signal level */
regex = g_regex_new ("\\r\\n\\^HRSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_evdo_quality_change, modem, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_evdo_quality_change, modem, NULL);
g_regex_unref (regex);
}
}
diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c
index d450f25..5123e7f 100644
--- a/plugins/mm-modem-huawei-gsm.c
+++ b/plugins/mm-modem-huawei-gsm.c
@@ -11,38 +11,40 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <errno.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#include "mm-modem-huawei-gsm.h"
#include "mm-modem-gsm-network.h"
+#include "mm-modem-gsm-card.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
#include "mm-serial-parsers.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);
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_NETWORK, modem_gsm_network_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init))
#define MM_MODEM_HUAWEI_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmPrivate))
typedef struct {
/* Cached state */
- guint signal_quality;
- MMModemGsmMode mode;
- MMModemGsmBand band;
+ guint32 band;
} MMModemHuaweiGsmPrivate;
MMModem *
@@ -61,6 +63,61 @@ mm_modem_huawei_gsm_new (const char *device,
NULL));
}
+/*****************************************************************************/
+
+typedef struct {
+ MMModemGsmBand mm;
+ guint32 huawei;
+} BandTable;
+
+static BandTable bands[] = {
+ /* Sort 3G first since it's preferred */
+ { MM_MODEM_GSM_BAND_U2100, 0x00400000 },
+ { MM_MODEM_GSM_BAND_U1900, 0x00800000 },
+ { MM_MODEM_GSM_BAND_U850, 0x04000000 },
+ { MM_MODEM_GSM_BAND_U900, 0x00020000 },
+ { MM_MODEM_GSM_BAND_G850, 0x00080000 },
+ /* 2G second */
+ { MM_MODEM_GSM_BAND_DCS, 0x00000080 },
+ { MM_MODEM_GSM_BAND_EGSM, 0x00000300 }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */
+ { MM_MODEM_GSM_BAND_PCS, 0x00200000 },
+ /* And ANY last since it's most inclusive */
+ { MM_MODEM_GSM_BAND_ANY, 0x3FFFFFFF },
+};
+
+static gboolean
+band_mm_to_huawei (MMModemGsmBand band, guint32 *out_huawei)
+{
+ int i;
+
+ for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) {
+ if (bands[i].mm == band) {
+ *out_huawei = bands[i].huawei;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+band_huawei_to_mm (guint32 huawei, MMModemGsmBand *out_mm)
+{
+ int i;
+
+ for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) {
+ /* The dongle returns a bitfield, but since we don't support that
+ * yet in MM, take the "best" band and return it.
+ */
+ if (bands[i].huawei & huawei) {
+ *out_mm = bands[i].mm;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*****************************************************************************/
+
static gboolean
parse_syscfg (MMModemHuaweiGsm *self,
const char *reply,
@@ -68,31 +125,31 @@ parse_syscfg (MMModemHuaweiGsm *self,
int *mode_b,
guint32 *band,
int *unknown1,
- int *unknown2)
+ int *unknown2,
+ MMModemGsmAllowedMode *out_mode)
{
if (reply == NULL || strncmp (reply, "^SYSCFG:", 8))
return FALSE;
if (sscanf (reply + 8, "%d,%d,%x,%d,%d", mode_a, mode_b, band, unknown1, unknown2)) {
MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
-
+ MMModemGsmAllowedMode new_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+
/* Network mode */
if (*mode_a == 2 && *mode_b == 1)
- priv->mode = MM_MODEM_GSM_MODE_2G_PREFERRED;
+ new_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
else if (*mode_a == 2 && *mode_b == 2)
- priv->mode = MM_MODEM_GSM_MODE_3G_PREFERRED;
+ new_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
else if (*mode_a == 13 && *mode_b == 1)
- priv->mode = MM_MODEM_GSM_MODE_2G_ONLY;
+ new_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
else if (*mode_a == 14 && *mode_b == 2)
- priv->mode = MM_MODEM_GSM_MODE_3G_ONLY;
+ new_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+
+ if (out_mode)
+ *out_mode = new_mode;
/* Band */
- if (*band == 0x3FFFFFFF)
- priv->band = MM_MODEM_GSM_BAND_ANY;
- else if (*band == 0x400380)
- priv->band = MM_MODEM_GSM_BAND_DCS;
- else if (*band == 0x200000)
- priv->band = MM_MODEM_GSM_BAND_PCS;
+ priv->band = *band;
return TRUE;
}
@@ -101,165 +158,108 @@ parse_syscfg (MMModemHuaweiGsm *self,
}
static void
-set_network_mode_done (MMSerialPort *port,
+set_allowed_mode_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem);
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
if (error)
info->error = g_error_copy (error);
- else
- /* Success, cache the value */
- priv->mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"));
mm_callback_info_schedule (info);
}
static void
-set_network_mode_get_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
- if (error) {
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- } else {
- int a, b, u1, u2;
- guint32 band;
-
- if (parse_syscfg (MM_MODEM_HUAWEI_GSM (info->modem), response->str, &a, &b, &band, &u1, &u2)) {
- char *command;
-
- switch (GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"))) {
- case MM_MODEM_GSM_MODE_ANY:
- a = 2;
- b = 0;
- break;
- case MM_MODEM_GSM_MODE_GPRS:
- case MM_MODEM_GSM_MODE_EDGE:
- case MM_MODEM_GSM_MODE_2G_ONLY:
- a = 13;
- b = 1;
- break;
- case MM_MODEM_GSM_MODE_UMTS:
- case MM_MODEM_GSM_MODE_HSDPA:
- case MM_MODEM_GSM_MODE_HSUPA:
- case MM_MODEM_GSM_MODE_HSPA:
- case MM_MODEM_GSM_MODE_3G_ONLY:
- a = 14;
- b = 2;
- break;
- case MM_MODEM_GSM_MODE_2G_PREFERRED:
- a = 2;
- b = 1;
- break;
- case MM_MODEM_GSM_MODE_3G_PREFERRED:
- a = 2;
- b = 2;
- break;
- default:
- break;
- }
-
- command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2);
- mm_serial_port_queue_command (port, command, 3, set_network_mode_done, info);
- g_free (command);
- }
- }
-}
-
-static void
-set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *port;
+ int a, b;
+ char *command;
- info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
- switch (mode) {
- case MM_MODEM_GSM_MODE_ANY:
- case MM_MODEM_GSM_MODE_GPRS:
- case MM_MODEM_GSM_MODE_EDGE:
- case MM_MODEM_GSM_MODE_UMTS:
- case MM_MODEM_GSM_MODE_HSDPA:
- case MM_MODEM_GSM_MODE_HSUPA:
- case MM_MODEM_GSM_MODE_HSPA:
- case MM_MODEM_GSM_MODE_2G_PREFERRED:
- case MM_MODEM_GSM_MODE_3G_PREFERRED:
- case MM_MODEM_GSM_MODE_2G_ONLY:
- case MM_MODEM_GSM_MODE_3G_ONLY:
- /* Allowed values */
- mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, set_network_mode_get_done, info);
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
return;
+ }
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ a = 13;
+ b = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ a = 14;
+ b = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ a = 2;
+ b = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ a = 2;
+ b = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
default:
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode.");
+ a = 2;
+ b = 0;
break;
}
- mm_callback_info_schedule (info);
+ command = g_strdup_printf ("AT^SYSCFG=%d,%d,40000000,2,4", a, b);
+ mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info);
+ g_free (command);
}
static void
-get_network_mode_done (MMSerialPort *port,
+get_allowed_mode_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem);
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
int mode_a, mode_b, u1, u2;
guint32 band;
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
if (error)
info->error = g_error_copy (error);
- else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2))
- mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL);
+ else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2, &mode))
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
mm_callback_info_schedule (info);
}
static void
-get_network_mode (MMModemGsmNetwork *modem,
+get_allowed_mode (MMGenericGsm *gsm,
MMModemUIntFn callback,
gpointer user_data)
{
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
- if (priv->mode != MM_MODEM_GSM_MODE_ANY) {
- /* have cached mode (from an unsolicited message). Use that */
- MMCallbackInfo *info;
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL);
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
mm_callback_info_schedule (info);
- } else {
- /* Get it from modem */
- MMCallbackInfo *info;
- MMSerialPort *primary;
-
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, get_network_mode_done, info);
+ return;
}
+
+ mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_allowed_mode_done, info);
}
static void
-set_band_done (MMSerialPort *port,
+set_band_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -270,108 +270,63 @@ set_band_done (MMSerialPort *port,
if (error)
info->error = g_error_copy (error);
- else
+ else {
/* Success, cache the value */
priv->band = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band"));
+ }
mm_callback_info_schedule (info);
}
static void
-set_band_get_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem);
-
- if (error) {
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- } else {
- int a, b, u1, u2;
- guint32 band;
-
- if (parse_syscfg (self, response->str, &a, &b, &band, &u1, &u2)) {
- char *command;
-
- switch (GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band"))) {
- case MM_MODEM_GSM_BAND_ANY:
- band = 0x3FFFFFFF;
- break;
- case MM_MODEM_GSM_BAND_EGSM:
- band = 0x100;
- break;
- case MM_MODEM_GSM_BAND_DCS:
- band = 0x80;
- break;
- case MM_MODEM_GSM_BAND_U2100:
- band = 0x400000;
- break;
- case MM_MODEM_GSM_BAND_PCS:
- band = 0x200000;
- break;
- case MM_MODEM_GSM_BAND_G850:
- band = 0x80000;
- break;
- default:
- break;
- }
-
- command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2);
- mm_serial_port_queue_command (port, command, 3, set_band_done, info);
- g_free (command);
- }
- }
-}
-
-static void
set_band (MMModemGsmNetwork *modem,
MMModemGsmBand band,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *port;
+ char *command;
+ guint32 huawei_band = 0x3FFFFFFF;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- switch (band) {
- case MM_MODEM_GSM_BAND_ANY:
- case MM_MODEM_GSM_BAND_EGSM:
- case MM_MODEM_GSM_BAND_DCS:
- case MM_MODEM_GSM_BAND_U2100:
- case MM_MODEM_GSM_BAND_PCS:
- mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (band), NULL);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, set_band_get_done, info);
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
return;
- default:
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band.");
- break;
}
- mm_callback_info_schedule (info);
+ if (!band_mm_to_huawei (band, &huawei_band)) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band.");
+ mm_callback_info_schedule (info);
+ } else {
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (huawei_band), NULL);
+ command = g_strdup_printf ("AT^SYSCFG=16,3,%X,2,4", huawei_band);
+ mm_at_serial_port_queue_command (port, command, 3, set_band_done, info);
+ g_free (command);
+ }
}
static void
-get_band_done (MMSerialPort *port,
+get_band_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem);
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
int mode_a, mode_b, u1, u2;
guint32 band;
if (error)
info->error = g_error_copy (error);
- else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2))
- mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL);
+ else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2, NULL)) {
+ MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY;
+
+ band_huawei_to_mm (band, &mm_band);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
+ }
mm_callback_info_schedule (info);
}
@@ -382,84 +337,289 @@ get_band (MMModemGsmNetwork *modem,
gpointer user_data)
{
MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem);
- MMSerialPort *primary;
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- if (priv->band != MM_MODEM_GSM_BAND_ANY) {
- /* have cached mode (from an unsolicited message). Use that */
- MMCallbackInfo *info;
+ /* Prefer cached band from unsolicited messages if we have it */
+ if (priv->band != 0) {
+ MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_ANY;
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL);
+ band_huawei_to_mm (priv->band, &mm_band);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL);
mm_callback_info_schedule (info);
- } else {
- /* Get it from modem */
- MMCallbackInfo *info;
+ return;
+ }
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, get_band_done, info);
+ /* Otherwise ask the modem */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
+
+ mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_band_done, info);
}
static void
-get_signal_quality (MMModemGsmNetwork *modem,
+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;
+ GRegex *r = NULL;
+ GMatchInfo *match_info = NULL;
+ char *str;
+ int srv_stat = 0;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ /* Can't just use \d here since sometimes you get "^SYSINFO:2,1,0,3,1,,3" */
+ r = g_regex_new ("\\^SYSINFO:\\s*(\\d?),(\\d?),(\\d?),(\\d?),(\\d?),(\\d?),(\\d?)$", G_REGEX_UNGREEDY, 0, NULL);
+ if (!r) {
+ g_set_error_literal (&info->error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse ^SYSINFO results.");
+ goto done;
+ }
+
+ if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) {
+ g_set_error_literal (&info->error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse ^SYSINFO results.");
+ goto done;
+ }
+
+ str = g_match_info_fetch (match_info, 1);
+ if (str && strlen (str))
+ srv_stat = atoi (str);
+ g_free (str);
+
+ if (srv_stat != 0) {
+ /* Valid service */
+ str = g_match_info_fetch (match_info, 7);
+ if (str && strlen (str)) {
+ if (str[0] == '1')
+ act = MM_MODEM_GSM_ACCESS_TECH_GSM;
+ else if (str[0] == '2')
+ act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ else if (str[0] == '3')
+ act = MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ else if (str[0] == '4')
+ act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ else if (str[0] == '5')
+ act = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ else if (str[0] == '6')
+ act = MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+ else if (str[0] == '7')
+ act = MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ }
+ g_free (str);
+ }
+
+done:
+ if (match_info)
+ g_match_info_free (match_info);
+ if (r)
+ g_regex_unref (r);
+ 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, "^SYSINFO", 3, get_act_request_done, info);
+}
+
+/*****************************************************************************/
+
+static gboolean
+parse_num (const char *str, guint32 *out_num, guint32 min, guint32 max)
+{
+ unsigned long int tmp;
+
+ if (!str || !strlen (str))
+ return FALSE;
+
+ errno = 0;
+ tmp = strtoul (str, NULL, 10);
+ if (errno != 0 || tmp < min || tmp > max)
+ return FALSE;
+ *out_num = (guint32) tmp;
+ return TRUE;
+}
+
+static void
+send_huawei_cpin_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GRegex *r = NULL;
+ GMatchInfo *match_info = NULL;
+ const char *pin_type;
+ guint32 attempts_left = 0;
+ char *str = NULL;
+ guint32 num = 0;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ pin_type = mm_callback_info_get_data (info, "pin_type");
+
+ r = g_regex_new ("\\^CPIN:\\s*([^,]+),[^,]*,(\\d+),(\\d+),(\\d+),(\\d+)", G_REGEX_UNGREEDY, 0, NULL);
+ if (!r) {
+ g_set_error_literal (&info->error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse ^CPIN results (error creating regex).");
+ goto done;
+ }
+
+ if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) {
+ g_set_error_literal (&info->error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse ^CPIN results (match failed).");
+ goto done;
+ }
+
+ if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK))
+ num = 2;
+ else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN))
+ num = 3;
+ else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2))
+ num = 4;
+ else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2))
+ num = 5;
+ else {
+ g_debug ("%s: unhandled pin type '%s'", __func__, pin_type);
+
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type");
+ }
+
+ if (num > 0) {
+ gboolean success = FALSE;
+
+ str = g_match_info_fetch (match_info, num);
+ if (str) {
+ success = parse_num (str, &attempts_left, 0, 10);
+ g_free (str);
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse ^CPIN results (missing or invalid match info).");
+ }
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL);
+
+ g_match_info_free (match_info);
+
+done:
+ if (r)
+ g_regex_unref (r);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_unlock_retries (MMModemGsmCard *modem,
+ const char *pin_type,
MMModemUIntFn callback,
gpointer user_data)
{
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem);
+ MMAtSerialPort *port;
+ char *command;
+ MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- if (priv->signal_quality) {
- /* have cached signal quality (from an unsolicited message). Use that */
- MMCallbackInfo *info;
+ g_debug ("%s: pin type '%s'", __func__, pin_type);
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL);
+ /* Ensure we have a usable port to use for the command */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
mm_callback_info_schedule (info);
- } else {
- /* Use the generic implementation */
- MMModemGsmNetwork *parent_gsm_network_iface;
+ return;
+ }
- parent_gsm_network_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (modem));
- parent_gsm_network_iface->get_signal_quality (modem, callback, user_data);
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
}
+
+ /* if the modem have not yet been enabled we need to make sure echoing is turned off */
+ command = g_strdup_printf ("E0");
+ mm_at_serial_port_queue_command (port, command, 3, NULL, NULL);
+ g_free (command);
+
+ mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free);
+
+ command = g_strdup_printf ("^CPIN?");
+ mm_at_serial_port_queue_command (port, command, 3, send_huawei_cpin_done, info);
+ g_free (command);
}
+/*****************************************************************************/
/* Unsolicited message handlers */
static void
-handle_signal_quality_change (MMSerialPort *port,
+handle_signal_quality_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data);
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
char *str;
- int quality;
+ int quality = 0;
str = g_match_info_fetch (match_info, 1);
quality = atoi (str);
g_free (str);
- if (quality == 99)
+ if (quality == 99) {
/* 99 means unknown */
quality = 0;
- else
+ } else {
/* Normalize the quality */
- quality = quality * 100 / 31;
+ quality = CLAMP (quality, 0, 31) * 100 / 31;
+ }
- g_debug ("Signal quality: %d", quality);
- priv->signal_quality = (guint32) quality;
- mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), (guint32) quality);
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (self), (guint32) quality);
}
static void
-handle_mode_change (MMSerialPort *port,
+handle_mode_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data);
- MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self);
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
char *str;
int a;
int b;
@@ -472,29 +632,35 @@ handle_mode_change (MMSerialPort *port,
b = atoi (str);
g_free (str);
- if (a == 3 && b == 2)
- priv->mode = MM_MODEM_GSM_MODE_GPRS;
- else if (a == 3 && b == 3)
- priv->mode = MM_MODEM_GSM_MODE_EDGE;
- else if (a == 5 && b == 4)
- priv->mode = MM_MODEM_GSM_MODE_UMTS;
- else if (a == 5 && b == 5)
- priv->mode = MM_MODEM_GSM_MODE_HSDPA;
- else if (a == 5 && b == 6)
- priv->mode = MM_MODEM_GSM_MODE_HSUPA;
- else if (a == 5 && b == 7)
- priv->mode = MM_MODEM_GSM_MODE_HSPA;
+ if (a == 3) { /* GSM/GPRS mode */
+ if (b == 1)
+ act = MM_MODEM_GSM_ACCESS_TECH_GSM;
+ else if (b == 2)
+ act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ else if (b == 3)
+ act = MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ } else if (a == 5) { /* WCDMA mode */
+ if (b == 4)
+ act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ else if (b == 5)
+ act = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ else if (b == 6)
+ act = MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+ else if (b == 7)
+ act = MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ } else if (a == 0)
+ act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
else {
g_warning ("Couldn't parse mode change value: '%s'", str);
return;
}
- g_debug ("Mode: %d", priv->mode);
- mm_modem_gsm_network_mode (MM_MODEM_GSM_NETWORK (self), priv->mode);
+ g_debug ("Access Technology: %d", act);
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act);
}
static void
-handle_status_change (MMSerialPort *port,
+handle_status_change (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
@@ -546,38 +712,35 @@ grab_port (MMModem *modem,
}
if (usbif == 0) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY))
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
ptype = MM_PORT_TYPE_PRIMARY;
} else if (suggested_type == MM_PORT_TYPE_SECONDARY) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY))
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
ptype = MM_PORT_TYPE_SECONDARY;
}
port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
- if (port && MM_IS_SERIAL_PORT (port)) {
- g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
- if (ptype == MM_PORT_TYPE_SECONDARY) {
- GRegex *regex;
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
+ GRegex *regex;
- mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE);
+ g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
- regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_signal_quality_change, modem, NULL);
- g_regex_unref (regex);
+ regex = g_regex_new ("\\r\\n\\^RSSI:(\\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, handle_signal_quality_change, modem, NULL);
+ g_regex_unref (regex);
- regex = g_regex_new ("\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_mode_change, modem, NULL);
- g_regex_unref (regex);
+ regex = g_regex_new ("\\r\\n\\^MODE:(\\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, handle_mode_change, modem, NULL);
+ g_regex_unref (regex);
- regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_status_change, modem, NULL);
- g_regex_unref (regex);
+ regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, handle_status_change, modem, NULL);
+ g_regex_unref (regex);
- regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL);
- g_regex_unref (regex);
- }
+ regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, modem, NULL);
+ g_regex_unref (regex);
}
out:
@@ -598,11 +761,14 @@ modem_init (MMModem *modem_class)
static void
modem_gsm_network_init (MMModemGsmNetwork *class)
{
- class->set_network_mode = set_network_mode;
- class->get_network_mode = get_network_mode;
class->set_band = set_band;
class->get_band = get_band;
- class->get_signal_quality = get_signal_quality;
+}
+
+static void
+modem_gsm_card_init (MMModemGsmCard *class)
+{
+ class->get_unlock_retries = get_unlock_retries;
}
static void
@@ -614,8 +780,13 @@ static void
mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
mm_modem_huawei_gsm_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (object_class, sizeof (MMModemHuaweiGsmPrivate));
+
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-modem-longcheer-gsm.c b/plugins/mm-modem-longcheer-gsm.c
new file mode 100644
index 0000000..62980f7
--- /dev/null
+++ b/plugins/mm-modem-longcheer-gsm.c
@@ -0,0 +1,222 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "mm-modem-longcheer-gsm.h"
+#include "mm-at-serial-port.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
+
+G_DEFINE_TYPE (MMModemLongcheerGsm, mm_modem_longcheer_gsm, MM_TYPE_GENERIC_GSM)
+
+MMModem *
+mm_modem_longcheer_gsm_new (const char *device,
+ const char *driver,
+ const char *plugin)
+{
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_MODEM_LONGCHEER_GSM,
+ MM_MODEM_MASTER_DEVICE, device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_PLUGIN, plugin,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+get_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *p;
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ gint mododr = -1;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ p = mm_strip_tag (response->str, "+MODODR:");
+ if (!p) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the allowed mode response");
+ goto done;
+ }
+
+ mododr = atoi (p);
+ switch (mododr) {
+ case 1:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ break;
+ case 2:
+ case 4:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ break;
+ case 3:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ break;
+ default:
+ break;
+ }
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "AT+MODODR?", 3, get_allowed_mode_done, info);
+}
+
+static void
+set_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+ int mododr = 0;
+
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ mododr = 3;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ mododr = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ default:
+ mododr = 2;
+ break;
+ }
+
+ command = g_strdup_printf ("+MODODR=%d", mododr);
+ mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info);
+ g_free (command);
+}
+
+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 (error)
+ info->error = g_error_copy (error);
+ else {
+ p = mm_strip_tag (response->str, "+PSRAT:");
+ act = mm_gsm_string_to_access_tech (p);
+ }
+
+ 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, "+PSRAT", 3, get_act_request_done, info);
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_longcheer_gsm_init (MMModemLongcheerGsm *self)
+{
+}
+
+static void
+mm_modem_longcheer_gsm_class_init (MMModemLongcheerGsmClass *klass)
+{
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
+
+ mm_modem_longcheer_gsm_parent_class = g_type_class_peek_parent (klass);
+
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
+}
+
diff --git a/plugins/mm-modem-longcheer-gsm.h b/plugins/mm-modem-longcheer-gsm.h
new file mode 100644
index 0000000..5383c52
--- /dev/null
+++ b/plugins/mm-modem-longcheer-gsm.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef MM_MODEM_LONGCHEER_GSM_H
+#define MM_MODEM_LONGCHEER_GSM_H
+
+#include "mm-generic-gsm.h"
+
+#define MM_TYPE_MODEM_LONGCHEER_GSM (mm_modem_longcheer_gsm_get_type ())
+#define MM_MODEM_LONGCHEER_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LONGCHEER_GSM, MMModemLongcheerGsm))
+#define MM_MODEM_LONGCHEER_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_LONGCHEER_GSM, MMModemLongcheerGsmClass))
+#define MM_IS_MODEM_LONGCHEER_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LONGCHEER_GSM))
+#define MM_IS_MODEM_LONGCHEER_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_LONGCHEER_GSM))
+#define MM_MODEM_LONGCHEER_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_LONGCHEER_GSM, MMModemLongcheerGsmClass))
+
+typedef struct {
+ MMGenericGsm parent;
+} MMModemLongcheerGsm;
+
+typedef struct {
+ MMGenericGsmClass parent;
+} MMModemLongcheerGsmClass;
+
+GType mm_modem_longcheer_gsm_get_type (void);
+
+MMModem *mm_modem_longcheer_gsm_new (const char *device,
+ const char *driver,
+ const char *plugin);
+
+#endif /* MM_MODEM_LONGCHEER_H */
diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c
index 686b35c..7f6bc9c 100644
--- a/plugins/mm-modem-mbm.c
+++ b/plugins/mm-modem-mbm.c
@@ -1,11 +1,13 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
- * Copyright (C) 2008 Ericsson AB
+ * Copyright (C) 2008 - 2010 Ericsson AB
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
* Bjorn Runaker <bjorn.runaker@ericsson.com>
* Torgny Johansson <torgny.johansson@ericsson.com>
* Jonas Sjöquist <jonas.sjoquist@ericsson.com>
+ * Dan Williams <dcbw@redhat.com>
*
* 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
@@ -17,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <stdlib.h>
@@ -30,17 +28,20 @@
#include "mm-modem-mbm.h"
#include "mm-modem-simple.h"
+#include "mm-modem-gsm-card.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
static void modem_simple_init (MMModemSimple *class);
+static void modem_gsm_card_init (MMModemGsmCard *class);
G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, 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_SIMPLE, modem_simple_init))
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init))
#define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate))
@@ -54,11 +55,6 @@ G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0,
#define MBM_NETWORK_MODE_2G 5
#define MBM_NETWORK_MODE_3G 6
-#define MBM_ERINFO_2G_GPRS 1
-#define MBM_ERINFO_2G_EGPRS 2
-#define MBM_ERINFO_3G_UMTS 1
-#define MBM_ERINFO_3G_HSDPA 2
-
typedef struct {
guint reg_id;
gboolean have_emrdy;
@@ -83,12 +79,6 @@ mbm_modem_authenticate (MMModemMbm *self,
const char *password,
gpointer user_data);
-static const char *
-mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error);
-
-static uint
-mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error);
-
MMModem *
mm_modem_mbm_new (const char *device,
const char *driver,
@@ -124,16 +114,9 @@ register_done (gpointer user_data)
MMModemMbm *self = MM_MODEM_MBM (reg_data->modem);
MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self);
MMModemGsmNetwork *parent_modem_iface;
- MMSerialPort *primary;
priv->reg_id = 0;
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
-
- mm_serial_port_queue_command (primary, "+CREG=1", 3, NULL, NULL);
- mm_serial_port_queue_command (primary, "+CMER=3,0,0,1", 3, NULL, NULL);
-
parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self));
parent_modem_iface->do_register (MM_MODEM_GSM_NETWORK (self),
reg_data->network_id,
@@ -167,21 +150,16 @@ do_register (MMModemGsmNetwork *modem,
}
static int
-mbm_parse_network_mode (MMModemGsmMode network_mode)
+mbm_parse_allowed_mode (MMModemGsmAllowedMode network_mode)
{
switch (network_mode) {
- case MM_MODEM_GSM_MODE_ANY:
- case MM_MODEM_GSM_MODE_3G_PREFERRED:
- case MM_MODEM_GSM_MODE_2G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
return MBM_NETWORK_MODE_ANY;
- case MM_MODEM_GSM_MODE_GPRS:
- case MM_MODEM_GSM_MODE_EDGE:
- case MM_MODEM_GSM_MODE_2G_ONLY:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
return MBM_NETWORK_MODE_2G;
- case MM_MODEM_GSM_MODE_3G_ONLY:
- case MM_MODEM_GSM_MODE_HSDPA:
- case MM_MODEM_GSM_MODE_HSUPA:
- case MM_MODEM_GSM_MODE_HSPA:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
return MBM_NETWORK_MODE_3G;
default:
return MBM_NETWORK_MODE_ANY;
@@ -189,7 +167,7 @@ mbm_parse_network_mode (MMModemGsmMode network_mode)
}
static void
-mbm_set_network_mode_done (MMSerialPort *port,
+mbm_set_allowed_mode_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -203,92 +181,144 @@ mbm_set_network_mode_done (MMSerialPort *port,
}
static void
-set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
MMModemFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
char *command;
- MMSerialPort *primary;
+ MMAtSerialPort *port;
- info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
- command = g_strdup_printf ("+CFUN=%d", mbm_parse_network_mode (mode));
- mm_serial_port_queue_command (primary, command, 3, mbm_set_network_mode_done, info);
+ command = g_strdup_printf ("+CFUN=%d", mbm_parse_allowed_mode (mode));
+ mm_at_serial_port_queue_command (port, command, 3, mbm_set_allowed_mode_done, info);
g_free (command);
}
static void
-get_network_mode_done (MMSerialPort *port,
+mbm_erinfo_received (MMAtSerialPort *port,
+ GMatchInfo *info,
+ gpointer user_data)
+{
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ char *str;
+
+ str = g_match_info_fetch (info, 2);
+ if (str) {
+ switch (atoi (str)) {
+ case 1:
+ act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ break;
+ case 2:
+ act = MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ break;
+ default:
+ break;
+ }
+ }
+ g_free (str);
+
+ /* 3G modes take precedence */
+ str = g_match_info_fetch (info, 3);
+ if (str) {
+ switch (atoi (str)) {
+ case 1:
+ act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ break;
+ case 2:
+ act = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ break;
+ default:
+ break;
+ }
+ }
+ g_free (str);
+
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+}
+
+static void
+get_allowed_mode_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- char *erinfo;
- int mode = 0, gsm = 0, umts = 0;
gboolean parsed = FALSE;
- if (error) {
+ if (error)
info->error = g_error_copy (error);
- goto done;
- }
-
- erinfo = strstr (response->str, "*ERINFO:");
- if (!erinfo)
- goto done;
-
- if (sscanf (erinfo + 8, "%d,%d,%d", &mode, &gsm, &umts) != 3)
- goto done;
+ else if (!g_str_has_prefix (response->str, "CFUN: ")) {
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ int a;
- if (gsm || umts) {
- MMModemGsmMode mm_mode = MM_MODEM_GSM_MODE_ANY;
-
- if (gsm == MBM_ERINFO_2G_GPRS)
- mm_mode = MM_MODEM_GSM_MODE_GPRS;
- else if (gsm == MBM_ERINFO_2G_EGPRS)
- mm_mode = MM_MODEM_GSM_MODE_EDGE;
- else if (umts == MBM_ERINFO_3G_UMTS)
- mm_mode = MM_MODEM_GSM_MODE_UMTS;
- else if (umts == MBM_ERINFO_3G_HSDPA)
- mm_mode = MM_MODEM_GSM_MODE_HSDPA;
- else
- g_debug ("%s unknown network mode %d,%d", __FUNCTION__, gsm, umts);
+ a = atoi (response->str + 6);
+ if (a == MBM_NETWORK_MODE_2G)
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ else if (a == MBM_NETWORK_MODE_3G)
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
- mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_mode), NULL);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
parsed = TRUE;
}
-done:
- if (!error && !parsed) {
+ if (!error && !parsed)
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Could not parse network mode results");
- }
+ "Could not parse allowed mode results");
mm_callback_info_schedule (info);
}
static void
-get_network_mode (MMModemGsmNetwork *modem,
+get_allowed_mode (MMGenericGsm *gsm,
MMModemUIntFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *port;
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "*ERINFO?", 3, get_network_mode_done, info);
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "+CFUN?", 3, get_allowed_mode_done, info);
}
/*****************************************************************************/
/* Simple Modem class override functions */
/*****************************************************************************/
+static const char *
+mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error)
+{
+ GValue *value;
+
+ value = (GValue *) g_hash_table_lookup (properties, name);
+ if (!value)
+ return NULL;
+
+ if (G_VALUE_HOLDS_STRING (value))
+ return g_value_get_string (value);
+
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid property type for '%s': %s (string expected)",
+ name, G_VALUE_TYPE_NAME (value));
+
+ return NULL;
+}
+
static void
simple_connect (MMModemSimple *simple,
GHashTable *properties,
@@ -298,14 +328,10 @@ simple_connect (MMModemSimple *simple,
MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (simple);
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemSimple *parent_iface;
- uint network_mode = 0;
priv->username = mbm_simple_get_string_property (properties, "username", &info->error);
priv->password = mbm_simple_get_string_property (properties, "password", &info->error);
- network_mode = mbm_simple_get_uint_property (properties, "network_mode", &info->error);
- priv->network_mode = mbm_parse_network_mode (network_mode);
-
parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple));
parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info);
}
@@ -315,18 +341,22 @@ simple_connect (MMModemSimple *simple,
/*****************************************************************************/
static void
-mbm_enable_done (MMSerialPort *port,
+mbm_enable_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ /* Start unsolicited signal strength and access technology responses */
+ mm_at_serial_port_queue_command (port, "+CMER=3,0,0,1", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "*ERINFO=1", 3, NULL, NULL);
+
mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
}
static void
-mbm_enap0_done (MMSerialPort *port,
+mbm_enap0_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -339,12 +369,12 @@ mbm_enap0_done (MMSerialPort *port,
priv->network_mode = MBM_NETWORK_MODE_ANY;
command = g_strdup_printf ("+CFUN=%d", priv->network_mode);
- mm_serial_port_queue_command (port, command, 3, mbm_enable_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, mbm_enable_done, info);
g_free (command);
}
static void
-mbm_init_done (MMSerialPort *port,
+mbm_init_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -360,17 +390,17 @@ mbm_init_done (MMSerialPort *port,
if (!priv->network_mode)
priv->network_mode = MBM_NETWORK_MODE_ANY;
- mm_serial_port_queue_command (port, "*ENAP=0", 3, mbm_enap0_done, info);
+ mm_at_serial_port_queue_command (port, "*ENAP=0", 3, mbm_enap0_done, info);
}
static void
-do_init (MMSerialPort *port, MMCallbackInfo *info)
+do_init (MMAtSerialPort *port, MMCallbackInfo *info)
{
- mm_serial_port_queue_command (port, "&F E0 V1 X4 &C1 +CMEE=1", 3, mbm_init_done, info);
+ mm_at_serial_port_queue_command (port, "&F E0 V1 X4 &C1 +CMEE=1", 3, mbm_init_done, info);
}
static void
-mbm_emrdy_done (MMSerialPort *port,
+mbm_emrdy_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -378,11 +408,9 @@ mbm_emrdy_done (MMSerialPort *port,
MMCallbackInfo *info = user_data;
MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem);
- if ( error
- && error->domain == MM_SERIAL_ERROR
- && error->code == MM_SERIAL_RESPONSE_TIMEOUT) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
g_warning ("%s: timed out waiting for EMRDY response.", __func__);
- } else
+ else
priv->have_emrdy = TRUE;
do_init (port, info);
@@ -393,18 +421,18 @@ do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data)
{
MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self);
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
- primary = mm_generic_gsm_get_port (self, MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_gsm_get_at_port (self, MM_PORT_TYPE_PRIMARY);
g_assert (primary);
if (priv->have_emrdy) {
/* Modem is ready, no need to check EMRDY */
do_init (primary, info);
} else
- mm_serial_port_queue_command (primary, "*EMRDY?", 5, mbm_emrdy_done, info);
+ mm_at_serial_port_queue_command (primary, "*EMRDY?", 5, mbm_emrdy_done, info);
}
typedef struct {
@@ -414,10 +442,10 @@ typedef struct {
} DisableInfo;
static void
-disable_creg_cmer_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+disable_unsolicited_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMModem *parent_modem_iface;
@@ -433,7 +461,7 @@ disable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
DisableInfo *info;
info = g_malloc0 (sizeof (DisableInfo));
@@ -441,11 +469,11 @@ disable (MMModem *modem,
info->user_data = user_data;
info->modem = modem;
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
g_assert (primary);
- /* Turn off unsolicited +CIEV signal strength indicator */
- mm_serial_port_queue_command (primary, "+CREG=0;+CMER=0", 5, disable_creg_cmer_done, info);
+ /* Turn off unsolicited responses */
+ mm_at_serial_port_queue_command (primary, "+CMER=0;*ERINFO=0", 5, disable_unsolicited_done, info);
}
static void
@@ -466,29 +494,77 @@ do_connect (MMModem *modem,
}
static void
-disconnect (MMModem *modem,
- MMModemFn callback,
- gpointer user_data)
+do_disconnect (MMGenericGsm *gsm,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info;
- MMSerialPort *primary;
-
- mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
+ MMAtSerialPort *primary;
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY);
g_assert (primary);
- mm_serial_port_queue_command (primary, "*ENAP=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (primary, "*ENAP=0", 3, NULL, NULL);
- mm_generic_gsm_update_enabled_state (MM_GENERIC_GSM (modem), FALSE, MM_MODEM_STATE_REASON_NONE);
+ MM_GENERIC_GSM_CLASS (mm_modem_mbm_parent_class)->do_disconnect (gsm, cid, callback, user_data);
+}
- info = mm_callback_info_new (modem, callback, user_data);
+static void
+factory_reset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ mm_serial_port_close (MM_SERIAL_PORT (port));
mm_callback_info_schedule (info);
}
+static void
+factory_reset (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (self, callback, user_data);
+
+ /* Ensure we have a usable port to use for the command */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "&F +CMEE=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+COPS=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CR=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CRC=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CMER=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "*EPEE=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CNMI=2, 0, 0, 0, 0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CGREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "*EIAD=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CGSMS=3", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "+CSCA=\"\",129", 3, factory_reset_done, info);
+}
+
/*****************************************************************************/
static void
-mbm_emrdy_received (MMSerialPort *port,
+mbm_emrdy_received (MMAtSerialPort *port,
GMatchInfo *info,
gpointer user_data)
{
@@ -496,7 +572,7 @@ mbm_emrdy_received (MMSerialPort *port,
}
static void
-mbm_pacsp_received (MMSerialPort *port,
+mbm_pacsp_received (MMAtSerialPort *port,
GMatchInfo *info,
gpointer user_data)
{
@@ -504,65 +580,78 @@ mbm_pacsp_received (MMSerialPort *port,
}
static void
-mbm_ciev_received (MMSerialPort *port,
+mbm_ciev_received (MMAtSerialPort *port,
GMatchInfo *info,
gpointer user_data)
{
int quality = 0, ind = 0;
- const char *str;
+ char *str;
str = g_match_info_fetch (info, 1);
if (str)
ind = atoi (str);
+ g_free (str);
if (ind == MBM_SIGNAL_INDICATOR) {
str = g_match_info_fetch (info, 2);
if (str) {
quality = atoi (str);
- mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (user_data), quality * 20);
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), quality * 20);
}
+ g_free (str);
}
}
static void
-mbm_do_connect_done (MMModemMbm *self)
+mbm_do_connect_done (MMModemMbm *self, gboolean success)
{
MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self);
- if (priv->pending_connect_info) {
+ if (!priv->pending_connect_info)
+ return;
+
+ if (success)
mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), NULL, priv->pending_connect_info);
- priv->pending_connect_info = NULL;
+ else {
+ GError *connect_error;
+
+ connect_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_BUSY);
+ mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), connect_error, priv->pending_connect_info);
+ g_error_free (connect_error);
}
+ priv->pending_connect_info = NULL;
}
static void
-mbm_e2nap_received (MMSerialPort *port,
+mbm_e2nap_received (MMAtSerialPort *port,
GMatchInfo *info,
gpointer user_data)
{
int state = 0;
- const char *str;
+ char *str;
str = g_match_info_fetch (info, 1);
if (str)
state = atoi (str);
-
- if (MBM_E2NAP_DISCONNECTED == state)
- g_debug ("%s, disconnected", __func__);
- else if (MBM_E2NAP_CONNECTED == state) {
- g_debug ("%s, connected", __func__);
- mbm_do_connect_done (MM_MODEM_MBM (user_data));
+ g_free (str);
+
+ if (MBM_E2NAP_DISCONNECTED == state) {
+ g_debug ("%s: disconnected", __func__);
+ mbm_do_connect_done (MM_MODEM_MBM (user_data), FALSE);
+ } else if (MBM_E2NAP_CONNECTED == state) {
+ g_debug ("%s: connected", __func__);
+ mbm_do_connect_done (MM_MODEM_MBM (user_data), TRUE);
} else if (MBM_E2NAP_CONNECTING == state)
- g_debug("%s, connecting", __func__);
+ g_debug("%s: connecting", __func__);
else {
/* Should not happen */
- g_debug("%s, undefined e2nap status",__FUNCTION__);
- g_assert_not_reached ();
+ g_debug("%s: unhandled E2NAP state %d", __func__, state);
+ mbm_do_connect_done (MM_MODEM_MBM (user_data), FALSE);
}
}
static void
-enap_poll_response (MMSerialPort *port,
+enap_poll_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -597,20 +686,21 @@ static gboolean
enap_poll (gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMSerialPort *port = mm_generic_gsm_get_port (MM_GENERIC_GSM (info->modem), MM_PORT_TYPE_PRIMARY);
+ MMAtSerialPort *port;
+ port = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (info->modem), MM_PORT_TYPE_PRIMARY);
g_assert (port);
- mm_serial_port_queue_command (port, "AT*ENAP?", 3, enap_poll_response, user_data);
+ mm_at_serial_port_queue_command (port, "AT*ENAP?", 3, enap_poll_response, user_data);
/* we cancel this in the _done function if all is fine */
return TRUE;
}
static void
-enap_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+enap_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
guint tid;
@@ -623,12 +713,11 @@ enap_done (MMSerialPort *port,
tid = g_timeout_add_seconds (1, enap_poll, user_data);
/* remember poll id as callback info object, with source_remove as free func */
mm_callback_info_set_data (info, "mbm-enap-poll-id", GUINT_TO_POINTER (tid), (GFreeFunc) g_source_remove);
- mm_serial_port_queue_command (port, "AT*E2NAP=1", 3, NULL, NULL);
}
static void
mbm_auth_done (MMSerialPort *port,
- GString *response,
+ GByteArray *response,
GError *error,
gpointer user_data)
{
@@ -643,8 +732,11 @@ mbm_auth_done (MMSerialPort *port,
}
cid = mm_generic_gsm_get_cid (modem);
+
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT*E2NAP=1", 3, NULL, NULL);
+
command = g_strdup_printf ("AT*ENAP=1,%d", cid);
- mm_serial_port_queue_command (port, command, 3, enap_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), command, 3, enap_done, user_data);
g_free (command);
}
@@ -654,61 +746,133 @@ mbm_modem_authenticate (MMModemMbm *self,
const char *password,
gpointer user_data)
{
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY);
g_assert (primary);
if (username || password) {
- char *command;
+ GByteArray *command;
+ MMModemCharset cur_set;
+ char *tmp;
+
+ /* F3507g at least wants the username and password to be sent in the
+ * modem's current character set.
+ */
+ cur_set = mm_generic_gsm_get_charset (MM_GENERIC_GSM (self));
+
+ command = g_byte_array_sized_new (75);
+ tmp = g_strdup_printf ("AT*EIAAUW=%d,1,", mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)));
+ g_byte_array_append (command, (const guint8 *) tmp, strlen (tmp));
+ g_free (tmp);
+
+ if (username)
+ mm_modem_charset_byte_array_append (command, username, TRUE, cur_set);
+ else
+ g_byte_array_append (command, (const guint8 *) "\"\"", 2);
- command = g_strdup_printf ("*EIAAUW=%d,1,\"%s\",\"%s\"",
- mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)),
- username ? username : "",
- password ? password : "");
+ g_byte_array_append (command, (const guint8 *) ",", 1);
- mm_serial_port_queue_command (primary, command, 3, mbm_auth_done, user_data);
- g_free (command);
+ if (password)
+ mm_modem_charset_byte_array_append (command, password, TRUE, cur_set);
+ else
+ g_byte_array_append (command, (const guint8 *) "\"\"", 2);
+
+ g_byte_array_append (command, (const guint8 *) "\r", 1);
+
+ mm_serial_port_queue_command (MM_SERIAL_PORT (primary),
+ command,
+ TRUE,
+ 3,
+ mbm_auth_done,
+ user_data);
} else
- mbm_auth_done (primary, NULL, NULL, user_data);
+ mbm_auth_done (MM_SERIAL_PORT (primary), NULL, NULL, user_data);
}
-static const char *
-mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error)
+/*****************************************************************************/
+
+static void
+send_epin_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- GValue *value;
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *pin_type;
+ int attempts_left = 0;
- value = (GValue *) g_hash_table_lookup (properties, name);
- if (!value)
- return NULL;
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
- if (G_VALUE_HOLDS_STRING (value))
- return g_value_get_string (value);
+ pin_type = mm_callback_info_get_data (info, "pin_type");
- g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Invalid property type for '%s': %s (string expected)",
- name, G_VALUE_TYPE_NAME (value));
+ if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN))
+ sscanf (response->str, "*EPIN: %d", &attempts_left);
+ else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK))
+ sscanf (response->str, "*EPIN: %*d, %d", &attempts_left);
+ else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2))
+ sscanf (response->str, "*EPIN: %*d, %*d, %d", &attempts_left);
+ else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2))
+ sscanf (response->str, "*EPIN: %*d, %*d, %*d, %d", &attempts_left);
+ else {
+ g_debug ("%s: unhandled pin type '%s'", __func__, pin_type);
- return NULL;
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type");
+ }
+
+ if (attempts_left < 0 || attempts_left > 998) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid PIN attempts left %d", attempts_left);
+ attempts_left = 0;
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL);
+
+done:
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ mm_callback_info_schedule (info);
}
-static uint
-mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error)
+static void
+mbm_get_unlock_retries (MMModemGsmCard *modem,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
{
- GValue *value;
+ MMAtSerialPort *port;
+ char *command;
+ MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- value = (GValue *) g_hash_table_lookup (properties, name);
- if (!value)
- return 0;
+ g_debug ("%s: pin type '%s'", __func__, pin_type);
- if (G_VALUE_HOLDS_UINT (value))
- return g_value_get_uint (value);
+ /* Ensure we have a usable port to use for the command */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
- 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));
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* if the modem have not yet been enabled we need to make sure echoing is turned off */
+ command = g_strdup_printf ("E0");
+ mm_at_serial_port_queue_command (port, command, 3, NULL, NULL);
+ g_free (command);
- return 0;
+ mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free);
+
+ command = g_strdup_printf ("*EPIN?");
+ mm_at_serial_port_queue_command (port, command, 3, send_epin_done, info);
+ g_free (command);
}
/*****************************************************************************/
@@ -727,43 +891,52 @@ grab_port (MMModem *modem,
if (!strcmp (subsys, "tty")) {
if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY))
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
ptype = MM_PORT_TYPE_PRIMARY;
- else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY))
+ else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
ptype = MM_PORT_TYPE_SECONDARY;
} else
ptype = suggested_type;
}
port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
- if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) {
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
GRegex *regex;
- mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE);
+ 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);
+ g_regex_unref (regex);
- regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_emrdy_received, modem, NULL);
- g_regex_unref (regex);
+ /* Catch the extended error status bit of the command too */
+ 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);
+ g_regex_unref (regex);
+ }
- regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL);
+ regex = g_regex_new ("\\r\\n\\*EMRDY: \\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_emrdy_received, modem, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\+CIEV: (\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_ciev_received, modem, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, mbm_ciev_received, modem, NULL);
g_regex_unref (regex);
/* also consume unsolicited mbm messages we are not interested in them - see LP: #416418 */
- regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R", G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL);
+ regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R", G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ g_regex_unref (regex);
+
+ regex = g_regex_new ("\\r\\n\\*ERINFO:\\s*(\\d),(\\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, mbm_erinfo_received, modem, NULL);
g_regex_unref (regex);
}
@@ -773,11 +946,15 @@ grab_port (MMModem *modem,
/*****************************************************************************/
static void
+modem_gsm_card_init (MMModemGsmCard *class)
+{
+ class->get_unlock_retries = mbm_get_unlock_retries;
+}
+
+static void
modem_gsm_network_init (MMModemGsmNetwork *class)
{
class->do_register = do_register;
- class->get_network_mode = get_network_mode;
- class->set_network_mode = set_network_mode;
}
static void
@@ -792,7 +969,7 @@ modem_init (MMModem *modem_class)
modem_class->grab_port = grab_port;
modem_class->disable = disable;
modem_class->connect = do_connect;
- modem_class->disconnect = disconnect;
+ modem_class->factory_reset = factory_reset;
}
static void
@@ -826,5 +1003,8 @@ mm_modem_mbm_class_init (MMModemMbmClass *klass)
object_class->finalize = finalize;
gsm_class->do_enable = do_enable;
+ gsm_class->do_disconnect = do_disconnect;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->set_allowed_mode = set_allowed_mode;
}
diff --git a/plugins/mm-modem-mbm.h b/plugins/mm-modem-mbm.h
index 8756e47..db0f627 100644
--- a/plugins/mm-modem-mbm.h
+++ b/plugins/mm-modem-mbm.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#ifndef MM_MODEM_MBM_H
diff --git a/plugins/mm-modem-nokia.c b/plugins/mm-modem-nokia.c
index 677a089..eb90287 100644
--- a/plugins/mm-modem-nokia.c
+++ b/plugins/mm-modem-nokia.c
@@ -56,19 +56,19 @@ grab_port (MMModem *modem,
MMPort *port = NULL;
if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY))
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
ptype = MM_PORT_TYPE_PRIMARY;
- else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY))
+ else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
ptype = MM_PORT_TYPE_SECONDARY;
} else
ptype = suggested_type;
port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
- if (port && MM_IS_SERIAL_PORT (port)) {
- mm_serial_port_set_response_parser (MM_SERIAL_PORT (port),
- mm_serial_parser_v1_e1_parse,
- mm_serial_parser_v1_e1_new (),
- mm_serial_parser_v1_e1_destroy);
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
+ mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
+ mm_serial_parser_v1_e1_parse,
+ mm_serial_parser_v1_e1_new (),
+ mm_serial_parser_v1_e1_destroy);
}
return !!port;
diff --git a/plugins/mm-modem-novatel-cdma.c b/plugins/mm-modem-novatel-cdma.c
new file mode 100644
index 0000000..64ee15f
--- /dev/null
+++ b/plugins/mm-modem-novatel-cdma.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "mm-modem-novatel-cdma.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+
+static void modem_cdma_init (MMModemCdma *cdma_class);
+
+G_DEFINE_TYPE_EXTENDED (MMModemNovatelCdma, mm_modem_novatel_cdma, MM_TYPE_GENERIC_CDMA, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_CDMA, modem_cdma_init))
+
+
+MMModem *
+mm_modem_novatel_cdma_new (const char *device,
+ const char *driver,
+ const char *plugin,
+ gboolean evdo_rev0,
+ gboolean evdo_revA)
+{
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_MODEM_NOVATEL_CDMA,
+ MM_MODEM_MASTER_DEVICE, device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_PLUGIN, plugin,
+ MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0,
+ MM_GENERIC_CDMA_EVDO_REVA, evdo_revA,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+parent_csq_done (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (result), NULL);
+ mm_callback_info_schedule (info);
+}
+
+static int
+get_one_qual (const char *reply, const char *tag)
+{
+ int qual = -1;
+ const char *p;
+
+ p = strstr (reply, tag);
+ if (!p)
+ return -1;
+
+ /* Skip the tag */
+ p += strlen (tag);
+
+ /* Skip spaces */
+ while (isspace (*p))
+ p++;
+ if (*p == '-') {
+ long int dbm;
+
+ errno = 0;
+ dbm = strtol (p, NULL, 10);
+ if (dbm < 0 && errno == 0) {
+ dbm = CLAMP (dbm, -113, -51);
+ qual = 100 - ((dbm + 51) * 100 / (-113 + 51));
+ }
+ }
+
+ return qual;
+}
+
+static void
+get_rssi_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemCdma *parent_iface;
+ int qual;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ if (info->modem) {
+ /* Fallback to parent's method */
+ g_clear_error (&info->error);
+ parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (info->modem));
+ parent_iface->get_signal_quality (MM_MODEM_CDMA (info->modem), parent_csq_done, info);
+ } else
+ mm_callback_info_schedule (info);
+
+ return;
+ }
+
+ /* Parse the signal quality */
+ qual = get_one_qual (response->str, "RX0=");
+ if (qual < 0)
+ qual = get_one_qual (response->str, "RX1=");
+
+ if (qual >= 0) {
+ mm_callback_info_set_result (info, GUINT_TO_POINTER ((guint32) qual), NULL);
+ mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (info->modem), (guint32) qual);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "%s", "Could not parse signal quality results");
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_signal_quality (MMModemCdma *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ MMModemCdma *parent_iface;
+
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), NULL);
+ if (!port) {
+ /* Let the superclass handle it */
+ parent_iface = g_type_interface_peek_parent (MM_MODEM_CDMA_GET_INTERFACE (modem));
+ parent_iface->get_signal_quality (MM_MODEM_CDMA (modem), callback, user_data);
+ return;
+ }
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ /* Many Novatel CDMA cards don't report CSQ in standard 0 - 31 and the CSQ
+ * reply doesn't appear to be in positive dBm either; instead try the custom
+ * Novatel command for it.
+ */
+ mm_at_serial_port_queue_command (port, "$NWRSSI", 3, get_rssi_done, info);
+}
+
+/*****************************************************************************/
+
+static void
+modem_cdma_init (MMModemCdma *cdma_class)
+{
+ cdma_class->get_signal_quality = get_signal_quality;
+}
+
+static void
+mm_modem_novatel_cdma_init (MMModemNovatelCdma *self)
+{
+}
+
+static void
+mm_modem_novatel_cdma_class_init (MMModemNovatelCdmaClass *klass)
+{
+ mm_modem_novatel_cdma_parent_class = g_type_class_peek_parent (klass);
+}
+
diff --git a/plugins/mm-modem-novatel-cdma.h b/plugins/mm-modem-novatel-cdma.h
new file mode 100644
index 0000000..4d38d8e
--- /dev/null
+++ b/plugins/mm-modem-novatel-cdma.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_MODEM_NOVATEL_CDMA_H
+#define MM_MODEM_NOVATEL_CDMA_H
+
+#include "mm-generic-cdma.h"
+
+#define MM_TYPE_MODEM_NOVATEL_CDMA (mm_modem_novatel_cdma_get_type ())
+#define MM_MODEM_NOVATEL_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOVATEL_CDMA, MMModemNovatelCdma))
+#define MM_MODEM_NOVATEL_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOVATEL_CDMA, MMModemNovatelCdmaClass))
+#define MM_IS_MODEM_NOVATEL_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOVATEL_CDMA))
+#define MM_IS_MODEM_NOVATEL_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOVATEL_CDMA))
+#define MM_MODEM_NOVATEL_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOVATEL_CDMA, MMModemNovatelCdmaClass))
+
+typedef struct {
+ MMGenericCdma parent;
+} MMModemNovatelCdma;
+
+typedef struct {
+ MMGenericCdmaClass parent;
+} MMModemNovatelCdmaClass;
+
+GType mm_modem_novatel_cdma_get_type (void);
+
+MMModem *mm_modem_novatel_cdma_new (const char *device,
+ const char *driver,
+ const char *plugin,
+ gboolean evdo_rev0,
+ gboolean evdo_revA);
+
+#endif /* MM_MODEM_NOVATEL_CDMA_H */
diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c
index 8189627..584156f 100644
--- a/plugins/mm-modem-novatel-gsm.c
+++ b/plugins/mm-modem-novatel-gsm.c
@@ -11,9 +11,10 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -21,6 +22,7 @@
#include "mm-modem-novatel-gsm.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
static void modem_init (MMModem *modem_class);
@@ -49,114 +51,246 @@ mm_modem_novatel_gsm_new (const char *device,
/*****************************************************************************/
static void
-init_modem_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+dmat_callback2 (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
}
static void
-pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+dmat_callback (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMGenericGsm *self = MM_GENERIC_GSM (modem);
- MMSerialPort *primary;
-
if (error) {
- mm_generic_gsm_enable_complete (self, error, info);
- return;
+ /* Try it again */
+ if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL))
+ mm_at_serial_port_queue_command (port, "$NWDMAT=1", 2, dmat_callback2, NULL);
+ }
+
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+}
+
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType suggested_type,
+ gpointer user_data,
+ GError **error)
+{
+ MMGenericGsm *gsm = MM_GENERIC_GSM (modem);
+ MMPortType ptype = MM_PORT_TYPE_IGNORED;
+ MMPort *port = NULL;
+
+ if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
+ ptype = MM_PORT_TYPE_PRIMARY;
+ else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
+ ptype = MM_PORT_TYPE_SECONDARY;
+ } else
+ ptype = suggested_type;
+
+ port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
+ if (port && MM_IS_AT_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) {
+ /* Flip secondary ports to AT mode */
+ if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL))
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "$NWDMAT=1", 2, dmat_callback, NULL);
}
- /* Finish the initialization */
- primary = mm_generic_gsm_get_port (self, MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1", 10, init_modem_done, info);
+ return !!port;
}
+/*****************************************************************************/
+
static void
-pre_init_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+set_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error) {
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+ int nw_mode = 0; /* 3G preferred */
+
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
return;
}
- /* Now check the PIN explicitly, novatel doesn't seem to report
- * that it needs it otherwise.
- */
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info);
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ nw_mode = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ nw_mode = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ default:
+ break;
+ }
+
+ command = g_strdup_printf ("$NWRAT=%d,2", nw_mode);
+ mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info);
+ g_free (command);
+}
+
+static gboolean
+parse_nwrat_response (GString *response,
+ MMModemGsmAllowedMode *out_mode,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ char *str;
+ gint mode = -1;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (response != NULL, FALSE);
+ g_return_val_if_fail (out_mode != NULL, FALSE);
+
+ r = g_regex_new ("\\$NWRAT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL);
+ if (!r) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Internal error parsing mode/tech response");
+ return FALSE;
+ }
+
+ if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, NULL)) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to parse mode/tech response");
+ goto out;
+ }
+
+ str = g_match_info_fetch (match_info, 1);
+ mode = atoi (str);
+ g_free (str);
+
+ g_match_info_free (match_info);
+
+ if (mode < 0 || mode > 2) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to parse mode/tech response");
+ goto out;
+ }
+
+ if (out_mode) {
+ if (mode == 0)
+ *out_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ else if (mode == 1)
+ *out_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ else if (mode == 2)
+ *out_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ else
+ *out_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ }
+ success = TRUE;
+
+out:
+ g_regex_unref (r);
+ return success;
}
static void
-enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
+get_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info = user_data;
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
- if (error)
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
- else
- mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data);
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ parse_nwrat_response (response, &mode, &info->error);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
+ }
+
+ mm_callback_info_schedule (info);
}
static void
-do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data)
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *port;
- primary = mm_generic_gsm_get_port (modem, MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
- info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_flash (primary, 100, enable_flash_done, info);
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "$NWRAT?", 3, get_allowed_mode_done, info);
}
static void
-dmat_callback (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+get_act_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- mm_serial_port_close (port);
+ MMCallbackInfo *info = user_data;
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ const char *p;
+
+ 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 gboolean
-grab_port (MMModem *modem,
- const char *subsys,
- const char *name,
- MMPortType suggested_type,
- gpointer user_data,
- GError **error)
+static void
+get_access_technology (MMGenericGsm *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
{
- MMGenericGsm *gsm = MM_GENERIC_GSM (modem);
- MMPortType ptype = MM_PORT_TYPE_IGNORED;
- MMPort *port = NULL;
+ MMAtSerialPort *port;
+ MMCallbackInfo *info;
- if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY))
- ptype = MM_PORT_TYPE_PRIMARY;
- else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY))
- ptype = MM_PORT_TYPE_SECONDARY;
- } else
- ptype = suggested_type;
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
- if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) {
- /* Flip secondary ports to AT mode */
- if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL))
- mm_serial_port_queue_command (MM_SERIAL_PORT (port), "$NWDMAT=1", 2, dmat_callback, NULL);
+ port = mm_generic_gsm_get_best_at_port (modem, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
- return !!port;
+ mm_at_serial_port_queue_command (port, "$CNTI=0", 3, get_act_request_done, info);
}
/*****************************************************************************/
@@ -179,6 +313,8 @@ mm_modem_novatel_gsm_class_init (MMModemNovatelGsmClass *klass)
mm_modem_novatel_gsm_parent_class = g_type_class_peek_parent (klass);
- gsm_class->do_enable = do_enable;
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-modem-option-utils.c b/plugins/mm-modem-option-utils.c
new file mode 100644
index 0000000..35dd1ac
--- /dev/null
+++ b/plugins/mm-modem-option-utils.c
@@ -0,0 +1,451 @@
+/* -*- 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.
+ */
+
+/******************************************
+ * Generic utilities for Option NV modems
+ * Used with both 'option' and 'hso'
+ ******************************************/
+
+#include "mm-callback-info.h"
+#include "mm-at-serial-port.h"
+#include "mm-generic-gsm.h"
+#include "mm-modem-helpers.h"
+
+static void
+option_get_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ gboolean parsed = FALSE;
+
+ if (error)
+ info->error = g_error_copy (error);
+ else if (!g_str_has_prefix (response->str, "_OPSYS: ")) {
+ int a, b;
+
+ if (sscanf (response->str + 8, "%d,%d", &a, &b)) {
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+
+ switch (a) {
+ case 0:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ break;
+ case 1:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ break;
+ case 2:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ break;
+ case 3:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ break;
+ default:
+ break;
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
+ parsed = TRUE;
+ }
+ }
+
+ if (!error && !parsed)
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse allowed mode results");
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+option_get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ mm_at_serial_port_queue_command (port, "AT_OPSYS?", 3, option_get_allowed_mode_done, info);
+}
+
+static void
+option_set_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+option_set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+ int i;
+
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ i = 0;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ i = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ i = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ i = 3;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ default:
+ i = 5;
+ break;
+ }
+
+ command = g_strdup_printf ("AT_OPSYS=%d,2", i);
+ mm_at_serial_port_queue_command (port, command, 3, option_set_allowed_mode_done, info);
+ g_free (command);
+}
+
+static gboolean
+octi_to_mm (char octi, MMModemGsmAccessTech *out_act)
+{
+ if (octi == '1') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_GSM;
+ return TRUE;
+ } else if (octi == '2') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ return TRUE;
+ } else if (octi == '3') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+owcti_to_mm (char owcti, MMModemGsmAccessTech *out_act)
+{
+ if (owcti == '1') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ return TRUE;
+ } else if (owcti == '2') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ return TRUE;
+ } else if (owcti == '3') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+ return TRUE;
+ } else if (owcti == '4') {
+ *out_act = MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+parse_octi_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, "_OCTI:");
+
+ 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 && octi_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,
+ GError *error,
+ gpointer user_data)
+{
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+
+ if (!error) {
+ if (parse_octi_response (response, &act))
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+ }
+}
+
+static void
+ossys_owcti_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ const char *p;
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+
+ if (!error) {
+ p = mm_strip_tag (response->str, "_OWCTI:");
+ if (owcti_to_mm (*p, &act))
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+ }
+}
+
+static void
+option_ossys_tech_changed (MMAtSerialPort *port,
+ GMatchInfo *info,
+ gpointer user_data)
+{
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ 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;
+ }
+ }
+ g_free (str);
+
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+
+ /* _OSSYSI only indicates general 2G/3G mode, so queue up some explicit
+ * access technology requests.
+ */
+ if (act == MM_MODEM_GSM_ACCESS_TECH_GPRS)
+ mm_at_serial_port_queue_command (port, "_OCTI?", 3, ossys_octi_request_done, user_data);
+ else if (act == MM_MODEM_GSM_ACCESS_TECH_UMTS)
+ mm_at_serial_port_queue_command (port, "_OWCTI?", 3, ossys_owcti_request_done, user_data);
+}
+
+static void
+option_2g_tech_changed (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
+{
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ char *str;
+
+ str = g_match_info_fetch (match_info, 1);
+ if (octi_to_mm (str[0], &act))
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+ g_free (str);
+}
+
+static void
+option_3g_tech_changed (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
+{
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ char *str;
+
+ str = g_match_info_fetch (match_info, 1);
+ if (owcti_to_mm (str[0], &act))
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+ g_free (str);
+}
+
+static void
+option_signal_changed (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
+{
+ char *str;
+ int quality = 0;
+
+ str = g_match_info_fetch (match_info, 1);
+ quality = atoi (str);
+ g_free (str);
+
+ if (quality == 99) {
+ /* 99 means unknown */
+ quality = 0;
+ } else {
+ /* Normalize the quality */
+ quality = CLAMP (quality, 0, 31) * 100 / 31;
+ }
+
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), (guint32) quality);
+}
+
+static void
+option_register_unsolicted_handlers (MMGenericGsm *modem, MMAtSerialPort *port)
+{
+ GRegex *regex;
+
+ regex = g_regex_new ("\\r\\n_OSSYSI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_ossys_tech_changed, modem, NULL);
+ g_regex_unref (regex);
+
+ regex = g_regex_new ("\\r\\n_OCTI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_2g_tech_changed, modem, NULL);
+ g_regex_unref (regex);
+
+ regex = g_regex_new ("\\r\\n_OUWCTI:\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_3g_tech_changed, modem, NULL);
+ g_regex_unref (regex);
+
+ regex = g_regex_new ("\\r\\n_OSIGQ:\\s*(\\d+),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (port, regex, option_signal_changed, modem, NULL);
+ g_regex_unref (regex);
+}
+
+static void
+unsolicited_msg_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ if (info)
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+option_change_unsolicited_messages (MMGenericGsm *modem,
+ gboolean enabled,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = NULL;
+ MMAtSerialPort *primary;
+
+ if (callback) {
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ mm_callback_info_chain_start (info, 4);
+ }
+
+ primary = mm_generic_gsm_get_at_port (modem, MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
+
+ mm_at_serial_port_queue_command (primary, enabled ? "_OSSYS=1" : "_OSSYS=0", 3, unsolicited_msg_done, info);
+ mm_at_serial_port_queue_command (primary, enabled ? "_OCTI=1" : "_OCTI=0", 3, unsolicited_msg_done, info);
+ mm_at_serial_port_queue_command (primary, enabled ? "_OUWCTI=1" : "_OUWCTI=0", 3, unsolicited_msg_done, info);
+ mm_at_serial_port_queue_command (primary, enabled ? "_OSQI=1" : "_OSQI=0", 3, unsolicited_msg_done, info);
+}
+
+static void
+get_act_octi_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemGsmAccessTech octi = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ MMModemGsmAccessTech owcti;
+
+ 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)
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (octi), NULL);
+ }
+ }
+
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+get_act_owcti_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemGsmAccessTech owcti = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ const char *p;
+
+ 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);
+ }
+ }
+
+ mm_callback_info_chain_complete_one (info);
+}
+
+static void
+option_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);
+ mm_callback_info_chain_start (info, 2);
+
+ 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, "_OCTI?", 3, get_act_octi_request_done, info);
+ mm_at_serial_port_queue_command (port, "_OWCTI?", 3, get_act_owcti_request_done, info);
+}
+
diff --git a/plugins/mm-modem-option.c b/plugins/mm-modem-option.c
index 2076ae6..ac04b0b 100644
--- a/plugins/mm-modem-option.c
+++ b/plugins/mm-modem-option.c
@@ -23,12 +23,15 @@
#include "mm-callback-info.h"
static void modem_init (MMModem *modem_class);
-static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
G_DEFINE_TYPE_EXTENDED (MMModemOption, mm_modem_option, 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, modem_init))
+#define MM_MODEM_OPTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_OPTION, MMModemOptionPrivate))
+
+typedef struct {
+ guint enable_wait_id;
+} MMModemOptionPrivate;
MMModem *
mm_modem_option_new (const char *device,
@@ -46,174 +49,156 @@ mm_modem_option_new (const char *device,
NULL));
}
+#include "mm-modem-option-utils.c"
+
/*****************************************************************************/
-static void
-pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+static gboolean
+option_enabled (gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMCallbackInfo *info = user_data;
+ MMGenericGsm *modem;
+ MMModemOptionPrivate *priv;
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info);
-}
+ /* Make sure we don't use an invalid modem that may have been removed */
+ if (info->modem) {
+ modem = MM_GENERIC_GSM (info->modem);
+ priv = MM_MODEM_OPTION_GET_PRIVATE (modem);
+ priv->enable_wait_id = 0;
-static gboolean
-option_enabled (gpointer data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) data;
+ option_change_unsolicited_messages (modem, TRUE, NULL, NULL);
- /* Now check the PIN explicitly, option doesn't seem to report
- * that it needs it otherwise.
- */
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info);
+ MM_GENERIC_GSM_CLASS (mm_modem_option_parent_class)->do_enable_power_up_done (modem, NULL, NULL, info);
+ }
return FALSE;
}
static void
-parent_enable_done (MMModem *modem, GError *error, gpointer user_data)
+real_do_enable_power_up_done (MMGenericGsm *gsm,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemOptionPrivate *priv = MM_MODEM_OPTION_GET_PRIVATE (gsm);
if (error) {
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info);
+ /* Chain up to parent */
+ MM_GENERIC_GSM_CLASS (mm_modem_option_parent_class)->do_enable_power_up_done (gsm, NULL, error, info);
return;
}
- /* Option returns OK on +CFUN=1 right away but needs some time
- * to finish initialization
+ /* Some Option devices return OK on +CFUN=1 right away but need some time
+ * to finish initialization.
*/
- g_timeout_add_seconds (10, option_enabled, info);
+ g_warn_if_fail (priv->enable_wait_id == 0);
+ priv->enable_wait_id = g_timeout_add_seconds (10, option_enabled, info);
}
+/*****************************************************************************/
+
static void
-enable (MMModem *modem,
- MMModemFn callback,
- gpointer user_data)
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
{
- MMModem *parent_modem_iface;
- MMCallbackInfo *info;
+ option_get_allowed_mode (gsm, callback, user_data);
+}
- info = mm_callback_info_new (modem, callback, user_data);
- parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem));
- parent_modem_iface->enable (modem, parent_enable_done, info);
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ option_set_allowed_mode (gsm, mode, callback, user_data);
}
static void
-get_network_mode_done (MMSerialPort *port,
- GString *response,
- GError *error,
+get_access_technology (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
gpointer user_data)
{
+ option_get_access_technology (gsm, callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+parent_disable_done (MMModem *modem, GError *error, gpointer user_data)
+{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- gboolean parsed = FALSE;
if (error)
info->error = g_error_copy (error);
- else if (!g_str_has_prefix (response->str, "_OPSYS: ")) {
- int a, b;
-
- if (sscanf (response->str + 8, "%d,%d", &a, &b)) {
- MMModemGsmMode mode = MM_MODEM_GSM_MODE_ANY;
-
- switch (a) {
- case 0:
- mode = MM_MODEM_GSM_MODE_2G_ONLY;
- break;
- case 1:
- mode = MM_MODEM_GSM_MODE_3G_ONLY;
- break;
- case 2:
- mode = MM_MODEM_GSM_MODE_2G_PREFERRED;
- break;
- case 3:
- mode = MM_MODEM_GSM_MODE_3G_PREFERRED;
- break;
- default:
- break;
- }
-
- mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
- parsed = TRUE;
- }
- }
-
- if (!error && !parsed)
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Could not parse network mode results");
-
mm_callback_info_schedule (info);
}
static void
-get_network_mode (MMModemGsmNetwork *modem,
- MMModemUIntFn callback,
- gpointer user_data)
+unsolicited_disable_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
- MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMCallbackInfo *info = user_data;
+ MMModem *parent_modem_iface;
+ GError *tmp_error = NULL;
+
+ /* Handle modem removal, but ignore other errors */
+ if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) {
+ parent_disable_done (modem, error, user_data);
+ return;
+ } else if (!modem) {
+ tmp_error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_REMOVED,
+ "The modem was removed.");
+ parent_disable_done (modem, tmp_error, user_data);
+ g_error_free (tmp_error);
+ return;
+ }
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "AT_OPSYS?", 3, get_network_mode_done, info);
+ /* Chain up to parent */
+ parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem));
+ parent_modem_iface->disable (info->modem, parent_disable_done, info);
}
static void
-set_network_mode_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+disable (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMCallbackInfo *info;
- if (error)
- info->error = g_error_copy (error);
-
- mm_callback_info_schedule (info);
+ mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ /* Turn off unsolicited messages so they don't pile up in the modem */
+ option_change_unsolicited_messages (MM_GENERIC_GSM (modem), FALSE, unsolicited_disable_done, info);
}
-static void
-set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data)
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType suggested_type,
+ gpointer user_data,
+ GError **error)
{
- MMCallbackInfo *info;
- MMSerialPort *primary;
- char *command;
- int i;
-
- info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
-
- switch (mode) {
- case MM_MODEM_GSM_MODE_ANY:
- case MM_MODEM_GSM_MODE_GPRS:
- case MM_MODEM_GSM_MODE_EDGE:
- case MM_MODEM_GSM_MODE_2G_ONLY:
- i = 0;
- break;
- case MM_MODEM_GSM_MODE_UMTS:
- case MM_MODEM_GSM_MODE_HSDPA:
- case MM_MODEM_GSM_MODE_HSUPA:
- case MM_MODEM_GSM_MODE_HSPA:
- case MM_MODEM_GSM_MODE_3G_ONLY:
- i = 1;
- break;
- case MM_MODEM_GSM_MODE_2G_PREFERRED:
- i = 2;
- break;
- case MM_MODEM_GSM_MODE_3G_PREFERRED:
- i = 3;
- break;
- default:
- i = 5;
- break;
+ MMGenericGsm *gsm = MM_GENERIC_GSM (modem);
+ MMPort *port = NULL;
+
+ port = mm_generic_gsm_grab_port (gsm, subsys, name, suggested_type, error);
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
+ if (mm_port_get_port_type (port) == MM_PORT_TYPE_PRIMARY) {
+ GRegex *regex;
+
+ regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ g_regex_unref (regex);
+ }
+ option_register_unsolicted_handlers (gsm, MM_AT_SERIAL_PORT (port));
}
- command = g_strdup_printf ("AT_OPSYS=%d,2", i);
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, command, 3, set_network_mode_done, info);
- g_free (command);
+ return !!port;
}
/*****************************************************************************/
@@ -221,24 +206,37 @@ set_network_mode (MMModemGsmNetwork *modem,
static void
modem_init (MMModem *modem_class)
{
- modem_class->enable = enable;
+ modem_class->disable = disable;
+ modem_class->grab_port = grab_port;
}
static void
-modem_gsm_network_init (MMModemGsmNetwork *class)
+mm_modem_option_init (MMModemOption *self)
{
- class->set_network_mode = set_network_mode;
- class->get_network_mode = get_network_mode;
}
static void
-mm_modem_option_init (MMModemOption *self)
+dispose (GObject *object)
{
+ MMModemOptionPrivate *priv = MM_MODEM_OPTION_GET_PRIVATE (object);
+
+ if (priv->enable_wait_id)
+ g_source_remove (priv->enable_wait_id);
}
static void
mm_modem_option_class_init (MMModemOptionClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
+
mm_modem_option_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (object_class, sizeof (MMModemOptionPrivate));
+
+ object_class->dispose = dispose;
+ gsm_class->do_enable_power_up_done = real_do_enable_power_up_done;
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-modem-sierra-cdma.c b/plugins/mm-modem-sierra-cdma.c
index 4f3140b..fc62bf6 100644
--- a/plugins/mm-modem-sierra-cdma.c
+++ b/plugins/mm-modem-sierra-cdma.c
@@ -28,6 +28,7 @@
#include "mm-callback-info.h"
#include "mm-serial-port.h"
#include "mm-serial-parsers.h"
+#include "mm-modem-helpers.h"
G_DEFINE_TYPE (MMModemSierraCdma, mm_modem_sierra_cdma, MM_TYPE_GENERIC_CDMA)
@@ -75,13 +76,19 @@ mm_modem_sierra_cdma_new (const char *device,
#define SYS_MODE_NO_SERVICE_TAG "NO SRV"
#define SYS_MODE_EVDO_TAG "HDR"
#define SYS_MODE_1X_TAG "1x"
+#define SYS_MODE_CDMA_TAG "CDMA"
#define EVDO_REV_TAG "HDR Revision:"
#define SID_TAG "SID:"
static gboolean
-get_roam_value (const char *reply, const char *tag, gboolean *roaming)
+get_roam_value (const char *reply,
+ const char *tag,
+ gboolean is_eri,
+ gboolean *out_roaming)
{
char *p;
+ gboolean success;
+ guint32 ind = 0;
p = strstr (reply, tag);
if (!p)
@@ -90,11 +97,26 @@ get_roam_value (const char *reply, const char *tag, gboolean *roaming)
p += strlen (tag);
while (*p && isspace (*p))
p++;
+
+ /* Use generic ERI parsing if it's an ERI */
+ if (is_eri) {
+ success = mm_cdma_parse_eri (p, out_roaming, &ind, NULL);
+ if (success) {
+ /* Sierra redefines ERI 0, 1, and 2 */
+ if (ind == 0)
+ *out_roaming = FALSE; /* home */
+ else if (ind == 1 || ind == 2)
+ *out_roaming = TRUE; /* roaming */
+ }
+ return success;
+ }
+
+ /* If it's not an ERI, roaming is just true/false */
if (*p == '1') {
- *roaming = TRUE;
+ *out_roaming = TRUE;
return TRUE;
} else if (*p == '0') {
- *roaming = FALSE;
+ *out_roaming = FALSE;
return TRUE;
}
@@ -109,8 +131,14 @@ sys_mode_has_service (SysMode mode)
|| mode == SYS_MODE_EVDO_REVA);
}
+static gboolean
+sys_mode_is_evdo (SysMode mode)
+{
+ return (mode == SYS_MODE_EVDO_REV0 || mode == SYS_MODE_EVDO_REVA);
+}
+
static void
-status_done (MMSerialPort *port,
+status_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -122,16 +150,16 @@ status_done (MMSerialPort *port,
gboolean have_sid = FALSE;
SysMode evdo_mode = SYS_MODE_UNKNOWN;
SysMode sys_mode = SYS_MODE_UNKNOWN;
- gboolean cdma_1x_set = FALSE, evdo_set = FALSE;
+ gboolean evdo_roam = FALSE, cdma1x_roam = FALSE;
if (error) {
- info->error = g_error_copy (error);
+ /* Leave superclass' reg state alone if AT!STATUS isn't supported */
goto done;
}
lines = g_strsplit_set (response->str, "\n\r", 0);
if (!lines) {
- /* Whatever, just use default registration state */
+ /* Whatever, just use superclass' registration state */
goto done;
}
@@ -197,29 +225,10 @@ status_done (MMSerialPort *port,
}
/* Roaming */
- if (get_roam_value (*iter, ROAM_1X_TAG, &bool_val)) {
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info,
- bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING :
- MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
- cdma_1x_set = TRUE;
- }
- if (get_roam_value (*iter, ROAM_EVDO_TAG, &bool_val)) {
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info,
- bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING :
- MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
- evdo_set = TRUE;
- }
- if (get_roam_value (*iter, GENERIC_ROAM_TAG, &bool_val)) {
- MMModemCdmaRegistrationState reg_state;
-
- reg_state = bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING :
- MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
-
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state);
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state);
- cdma_1x_set = TRUE;
- evdo_set = TRUE;
- }
+ get_roam_value (*iter, ROAM_1X_TAG, TRUE, &cdma1x_roam);
+ get_roam_value (*iter, ROAM_EVDO_TAG, TRUE, &evdo_roam);
+ if (get_roam_value (*iter, GENERIC_ROAM_TAG, FALSE, &bool_val))
+ cdma1x_roam = evdo_roam = bool_val;
/* Current system mode */
p = strstr (*iter, SYS_MODE_TAG);
@@ -231,7 +240,8 @@ status_done (MMSerialPort *port,
sys_mode = SYS_MODE_NO_SERVICE;
else if (!strncmp (p, SYS_MODE_EVDO_TAG, strlen (SYS_MODE_EVDO_TAG)))
sys_mode = SYS_MODE_EVDO_REV0;
- else if (!strncmp (p, SYS_MODE_1X_TAG, strlen (SYS_MODE_1X_TAG)))
+ else if ( !strncmp (p, SYS_MODE_1X_TAG, strlen (SYS_MODE_1X_TAG))
+ || !strncmp (p, SYS_MODE_CDMA_TAG, strlen (SYS_MODE_CDMA_TAG)))
sys_mode = SYS_MODE_CDMA_1X;
}
@@ -259,24 +269,36 @@ status_done (MMSerialPort *port,
}
/* Update current system mode */
- if (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA) {
+ if (sys_mode_is_evdo (sys_mode)) {
/* Prefer the explicit EVDO mode from EVDO_REV_TAG */
if (evdo_mode != SYS_MODE_UNKNOWN)
sys_mode = evdo_mode;
}
priv->sys_mode = sys_mode;
- if (registered || have_sid || sys_mode_has_service (sys_mode)) {
- /* As a backup, if for some reason the registration states didn't get
- * figured out by parsing the status info, set some generic registration
- * states here.
- */
- if (!cdma_1x_set)
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
-
- /* Ensure EVDO registration mode is set if we're at least in EVDO mode */
- if (!evdo_set && (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA))
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ /* If the modem didn't report explicit registration with "Modem has
+ * registered" then get registration status by looking at either system
+ * mode or (for older devices that don't report that) just the SID.
+ */
+ if (!registered) {
+ if (sys_mode != SYS_MODE_UNKNOWN)
+ registered = sys_mode_has_service (sys_mode);
+ else
+ registered = have_sid;
+ }
+
+ if (registered) {
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info,
+ cdma1x_roam ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING :
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+
+ if (sys_mode_is_evdo (sys_mode)) {
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info,
+ evdo_roam ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING :
+ MM_MODEM_CDMA_REGISTRATION_STATE_HOME);
+ } else {
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ }
} else {
/* Not registered */
mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
@@ -289,35 +311,27 @@ done:
static void
query_registration_state (MMGenericCdma *cdma,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary, *secondary;
- MMSerialPort *port;
-
- port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY);
- secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY);
+ MMAtSerialPort *port;
- info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data);
-
- if (mm_port_get_connected (MM_PORT (primary))) {
- if (!secondary) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get query registration state while connected");
- mm_callback_info_schedule (info);
- return;
- }
+ info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data);
- /* Use secondary port if primary is connected */
- port = secondary;
+ port = mm_generic_cdma_get_best_at_port (cdma, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
- mm_serial_port_queue_command (port, "!STATUS", 3, status_done, info);
+ mm_at_serial_port_queue_command (port, "!STATUS", 3, status_done, info);
}
static void
-pcstate_done (MMSerialPort *port,
+pcstate_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -334,14 +348,14 @@ post_enable (MMGenericCdma *cdma,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data);
- primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_cdma_get_at_port (cdma, MM_PORT_TYPE_PRIMARY);
g_assert (primary);
- mm_serial_port_queue_command (primary, "!pcstate=1", 5, pcstate_done, info);
+ mm_at_serial_port_queue_command (primary, "!pcstate=1", 5, pcstate_done, info);
}
static void
@@ -350,14 +364,14 @@ post_disable (MMGenericCdma *cdma,
gpointer user_data)
{
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data);
- primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_cdma_get_at_port (cdma, MM_PORT_TYPE_PRIMARY);
g_assert (primary);
- mm_serial_port_queue_command (primary, "!pcstate=0", 5, pcstate_done, info);
+ mm_at_serial_port_queue_command (primary, "!pcstate=0", 5, pcstate_done, info);
}
/*****************************************************************************/
diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c
index ee82234..bf5df31 100644
--- a/plugins/mm-modem-sierra-gsm.c
+++ b/plugins/mm-modem-sierra-gsm.c
@@ -11,9 +11,10 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -21,12 +22,18 @@
#include "mm-modem-sierra-gsm.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
static void modem_init (MMModem *modem_class);
G_DEFINE_TYPE_EXTENDED (MMModemSierraGsm, mm_modem_sierra_gsm, MM_TYPE_GENERIC_GSM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init))
+#define MM_MODEM_SIERRA_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsmPrivate))
+
+typedef struct {
+ guint enable_wait_id;
+} MMModemSierraGsmPrivate;
MMModem *
mm_modem_sierra_gsm_new (const char *device,
@@ -45,54 +52,232 @@ mm_modem_sierra_gsm_new (const char *device,
}
/*****************************************************************************/
-/* Modem class override functions */
-/*****************************************************************************/
static void
-pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+get_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GRegex *r = NULL;
+ GMatchInfo *match_info;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ /* Example response: !SELRAT: 03, UMTS 3G Preferred */
+ r = g_regex_new ("!SELRAT:\\s*(\\d+).*$", 0, 0, NULL);
+ if (!r) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the allowed mode response");
+ goto done;
+ }
+
+ if (g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) {
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ char *str;
+
+ str = g_match_info_fetch (match_info, 1);
+ switch (atoi (str)) {
+ case 0:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ break;
+ case 1:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ break;
+ case 2:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ break;
+ case 3:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ break;
+ case 4:
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the allowed mode response: '%s'",
+ response->str);
+ break;
+ }
+ g_free (str);
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
+ }
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info);
+done:
+ if (r)
+ g_regex_unref (r);
+ mm_callback_info_schedule (info);
}
-static gboolean
-sierra_enabled (gpointer data)
+static void
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) data;
+ MMCallbackInfo *info;
+ MMAtSerialPort *primary;
- /* Now check the PIN explicitly, sierra doesn't seem to report
- * that it needs it otherwise.
- */
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info);
- return FALSE;
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
+
+ /* Sierra secondary ports don't have full AT command interpreters */
+ primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY);
+ if (!primary || mm_port_get_connected (MM_PORT (primary))) {
+ g_set_error_literal (&info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot perform this operation while connected");
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (primary, "!SELRAT?", 3, get_allowed_mode_done, info);
}
static void
-parent_enable_done (MMModem *modem, GError *error, gpointer user_data)
+set_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error) {
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info);
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *primary;
+ char *command;
+ int idx = 0;
+
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ /* Sierra secondary ports don't have full AT command interpreters */
+ primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY);
+ if (!primary || mm_port_get_connected (MM_PORT (primary))) {
+ g_set_error_literal (&info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot perform this operation while connected");
+ mm_callback_info_schedule (info);
return;
}
- /* Sierra returns OK on +CFUN=1 right away but needs some time
- * to finish initialization.
- */
- g_timeout_add_seconds (10, sierra_enabled, info);
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ idx = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ idx = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ idx = 4;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ idx = 3;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ default:
+ break;
+ }
+
+ command = g_strdup_printf ("!SELRAT=%d", idx);
+ mm_at_serial_port_queue_command (primary, command, 3, set_allowed_mode_done, info);
+ g_free (command);
}
static void
-enable (MMModem *modem, MMModemFn callback, gpointer user_data)
+get_act_request_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- MMModem *parent_modem_iface;
+ MMCallbackInfo *info = user_data;
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ const char *p;
+
+ 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_new (modem, callback, user_data);
- parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem));
- parent_modem_iface->enable (modem, parent_enable_done, 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);
+}
+
+/*****************************************************************************/
+/* Modem class override functions */
+/*****************************************************************************/
+
+static gboolean
+sierra_enabled (gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericGsm *modem;
+ MMModemSierraGsmPrivate *priv;
+
+ /* Make sure we don't use an invalid modem that may have been removed */
+ if (info->modem) {
+ modem = MM_GENERIC_GSM (info->modem);
+ priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (modem);
+ priv->enable_wait_id = 0;
+ MM_GENERIC_GSM_CLASS (mm_modem_sierra_gsm_parent_class)->do_enable_power_up_done (modem, NULL, NULL, info);
+ }
+ return FALSE;
+}
+
+static void
+real_do_enable_power_up_done (MMGenericGsm *gsm,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ MMModemSierraGsmPrivate *priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (gsm);
+
+ if (error) {
+ /* Chain up to parent */
+ MM_GENERIC_GSM_CLASS (mm_modem_sierra_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info);
+ return;
+ }
+
+ /* Some Sierra devices return OK on +CFUN=1 right away but need some time
+ * to finish initialization.
+ */
+ g_warn_if_fail (priv->enable_wait_id == 0);
+ priv->enable_wait_id = g_timeout_add_seconds (10, sierra_enabled, info);
}
static gboolean
@@ -108,18 +293,25 @@ grab_port (MMModem *modem,
MMPort *port;
if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY))
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
ptype = MM_PORT_TYPE_PRIMARY;
- else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY))
+ else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
ptype = MM_PORT_TYPE_SECONDARY;
} else
ptype = suggested_type;
port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
- if (port && MM_IS_SERIAL_PORT (port))
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
+ GRegex *regex;
+
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
+ regex = g_regex_new ("\\r\\n\\+PACSP0\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ g_regex_unref (regex);
+ }
+
return !!port;
}
@@ -128,7 +320,6 @@ grab_port (MMModem *modem,
static void
modem_init (MMModem *modem_class)
{
- modem_class->enable = enable;
modem_class->grab_port = grab_port;
}
@@ -138,8 +329,27 @@ mm_modem_sierra_gsm_init (MMModemSierraGsm *self)
}
static void
+dispose (GObject *object)
+{
+ MMModemSierraGsmPrivate *priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (object);
+
+ if (priv->enable_wait_id)
+ g_source_remove (priv->enable_wait_id);
+}
+
+static void
mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
+
mm_modem_sierra_gsm_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (object_class, sizeof (MMModemSierraGsmPrivate));
+
+ object_class->dispose = dispose;
+ gsm_class->do_enable_power_up_done = real_do_enable_power_up_done;
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-modem-simtech-gsm.c b/plugins/mm-modem-simtech-gsm.c
new file mode 100644
index 0000000..07820b3
--- /dev/null
+++ b/plugins/mm-modem-simtech-gsm.c
@@ -0,0 +1,471 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "mm-modem-simtech-gsm.h"
+#include "mm-at-serial-port.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
+
+static void modem_init (MMModem *modem_class);
+
+G_DEFINE_TYPE_EXTENDED (MMModemSimtechGsm, mm_modem_simtech_gsm, MM_TYPE_GENERIC_GSM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init))
+
+MMModem *
+mm_modem_simtech_gsm_new (const char *device,
+ const char *driver,
+ const char *plugin)
+{
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_MODEM_SIMTECH_GSM,
+ MM_MODEM_MASTER_DEVICE, device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_PLUGIN, plugin,
+ NULL));
+}
+
+/*****************************************************************************/
+
+#define ACQ_ORDER_TAG "acq-order"
+
+static void
+get_mode_pref_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *p;
+ gint modepref = -1;
+ guint32 acqord;
+ MMModemGsmAllowedMode allowed = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ p = mm_strip_tag (response->str, "+CNMP:");
+ if (!p) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the mode preference response");
+ goto done;
+ }
+
+ acqord = GPOINTER_TO_UINT (mm_callback_info_get_data (info, ACQ_ORDER_TAG));
+ modepref = atoi (p);
+
+ if (modepref == 2) {
+ /* Automatic */
+ if (acqord == 0)
+ allowed = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ else if (acqord == 1)
+ allowed = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ else if (acqord == 2)
+ allowed = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Unknown acqisition order preference %d",
+ acqord);
+ }
+ } else if (modepref == 13) {
+ /* GSM only */
+ allowed = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ } else if (modepref == 14) {
+ /* WCDMA only */
+ allowed = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Unknown mode preference %d",
+ modepref);
+ }
+
+done:
+ if (!info->error)
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (allowed), NULL);
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_acq_order_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ const char *p;
+ gint acqord = -1;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ p = mm_strip_tag (response->str, "+CNAOP:");
+ if (!p) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the acqisition order response");
+ goto done;
+ }
+
+ acqord = atoi (p);
+ if (acqord < 0 || acqord > 2) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Unknown acquisition order response %d",
+ acqord);
+ } else {
+ /* Cache the acquisition preference */
+ mm_callback_info_set_data (info, ACQ_ORDER_TAG, GUINT_TO_POINTER (acqord), NULL);
+ }
+
+done:
+ if (info->error)
+ mm_callback_info_schedule (info);
+ else
+ mm_at_serial_port_queue_command (port, "+CNMP?", 3, get_mode_pref_done, info);
+}
+
+static void
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "+CNAOP?", 3, get_acq_order_done, info);
+}
+
+static void
+set_acq_order_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_mode_pref_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint32 naop;
+ char *command;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ naop = GPOINTER_TO_UINT (mm_callback_info_get_data (info, ACQ_ORDER_TAG));
+ command = g_strdup_printf ("+CNAOP=%u", naop);
+ mm_at_serial_port_queue_command (port, command, 3, set_acq_order_done, info);
+ g_free (command);
+}
+
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+ guint32 nmp = 2; /* automatic mode preference */
+ guint32 naop = 0; /* automatic acquisition order */
+
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ nmp = 13;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ nmp = 14;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ naop = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ naop = 3;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ default:
+ break;
+ }
+
+ mm_callback_info_set_data (info, ACQ_ORDER_TAG, GUINT_TO_POINTER (naop), NULL);
+
+ command = g_strdup_printf ("+CNMP=%u", nmp);
+ mm_at_serial_port_queue_command (port, command, 3, set_mode_pref_done, info);
+ g_free (command);
+}
+
+static MMModemGsmAccessTech
+simtech_act_to_mm_act (int nsmod)
+{
+ if (nsmod == 1)
+ return MM_MODEM_GSM_ACCESS_TECH_GSM;
+ else if (nsmod == 2)
+ return MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ else if (nsmod == 3)
+ return MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ else if (nsmod == 4)
+ return MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ else if (nsmod == 5)
+ return MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ else if (nsmod == 6)
+ return MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+ else if (nsmod == 7)
+ return MM_MODEM_GSM_ACCESS_TECH_HSPA;
+
+ return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+}
+
+static void
+get_act_tech_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;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ p = mm_strip_tag (response->str, "+CNSMOD:");
+ if (p)
+ p = strchr (p, ',');
+
+ if (!p || !isdigit (*(p + 1))) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the access technology response");
+ } else {
+ act = simtech_act_to_mm_act (atoi (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, "AT+CNSMOD?", 3, get_act_tech_done, info);
+}
+
+static void
+handle_act_change (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data)
+{
+ MMModemSimtechGsm *self = MM_MODEM_SIMTECH_GSM (user_data);
+ MMModemGsmAccessTech act;
+ char *str;
+
+ str = g_match_info_fetch (match_info, 1);
+ if (str && strlen (str)) {
+ act = simtech_act_to_mm_act (atoi (str));
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act);
+ }
+ g_free (str);
+}
+
+/*****************************************************************************/
+
+static void
+real_do_enable_power_up_done (MMGenericGsm *gsm,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ if (!error) {
+ MMAtSerialPort *primary;
+
+ /* Enable unsolicited result codes */
+ primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
+
+ /* Autoreport access technology changes */
+ mm_at_serial_port_queue_command (primary, "+CNSMOD=1", 5, NULL, NULL);
+
+ /* Autoreport CSQ (first arg), and only report when it changes (second arg) */
+ mm_at_serial_port_queue_command (primary, "+AUTOCSQ=1,1", 5, NULL, NULL);
+ }
+
+ /* Chain up to parent */
+ MM_GENERIC_GSM_CLASS (mm_modem_simtech_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMModem *modem;
+ MMModemFn callback;
+ gpointer user_data;
+} DisableInfo;
+
+static void
+disable_unsolicited_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+
+{
+ MMModem *parent_modem_iface;
+ DisableInfo *info = user_data;
+
+ parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem));
+ parent_modem_iface->disable (info->modem, info->callback, info->user_data);
+ g_free (info);
+}
+
+static void
+disable (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *primary;
+ DisableInfo *info;
+
+ info = g_malloc0 (sizeof (DisableInfo));
+ info->callback = callback;
+ info->user_data = user_data;
+ info->modem = modem;
+
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
+
+ /* Turn off unsolicited responses */
+ mm_at_serial_port_queue_command (primary, "+CNSMOD=0;+AUTOCSQ=0", 5, disable_unsolicited_done, info);
+}
+
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ MMPortType suggested_type,
+ gpointer user_data,
+ GError **error)
+{
+ MMGenericGsm *gsm = MM_GENERIC_GSM (modem);
+ MMPortType ptype = MM_PORT_TYPE_IGNORED;
+ MMPort *port;
+
+ if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
+ ptype = MM_PORT_TYPE_PRIMARY;
+ else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
+ ptype = MM_PORT_TYPE_SECONDARY;
+ } else
+ ptype = suggested_type;
+
+ port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
+
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
+ GRegex *regex;
+
+ regex = g_regex_new ("\\r\\n\\+CNSMOD:\\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, handle_act_change, modem, NULL);
+ g_regex_unref (regex);
+ }
+
+ return !!port;
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ modem_class->disable = disable;
+ modem_class->grab_port = grab_port;
+}
+
+static void
+mm_modem_simtech_gsm_init (MMModemSimtechGsm *self)
+{
+}
+
+static void
+mm_modem_simtech_gsm_class_init (MMModemSimtechGsmClass *klass)
+{
+ MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass);
+
+ mm_modem_simtech_gsm_parent_class = g_type_class_peek_parent (klass);
+
+ gsm_class->do_enable_power_up_done = real_do_enable_power_up_done;
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
+}
+
diff --git a/plugins/mm-modem-simtech-gsm.h b/plugins/mm-modem-simtech-gsm.h
new file mode 100644
index 0000000..0ba3c43
--- /dev/null
+++ b/plugins/mm-modem-simtech-gsm.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_MODEM_SIMTECH_GSM_H
+#define MM_MODEM_SIMTECH_GSM_H
+
+#include "mm-generic-gsm.h"
+
+#define MM_TYPE_MODEM_SIMTECH_GSM (mm_modem_simtech_gsm_get_type ())
+#define MM_MODEM_SIMTECH_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIMTECH_GSM, MMModemSimtechGsm))
+#define MM_MODEM_SIMTECH_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIMTECH_GSM, MMModemSimtechGsmClass))
+#define MM_IS_MODEM_SIMTECH_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIMTECH_GSM))
+#define MM_IS_MODEM_SIMTECH_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SIMTECH_GSM))
+#define MM_MODEM_SIMTECH_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIMTECH_GSM, MMModemSimtechGsmClass))
+
+typedef struct {
+ MMGenericGsm parent;
+} MMModemSimtechGsm;
+
+typedef struct {
+ MMGenericGsmClass parent;
+} MMModemSimtechGsmClass;
+
+GType mm_modem_simtech_gsm_get_type (void);
+
+MMModem *mm_modem_simtech_gsm_new (const char *device,
+ const char *driver,
+ const char *plugin);
+
+#endif /* MM_MODEM_SIMTECH_H */
diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c
index 92c23ae..ba8a1db 100644
--- a/plugins/mm-modem-zte.c
+++ b/plugins/mm-modem-zte.c
@@ -11,9 +11,10 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -22,6 +23,7 @@
#include "mm-serial-port.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
static void modem_init (MMModem *modem_class);
@@ -32,6 +34,8 @@ G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0,
typedef struct {
gboolean init_retried;
+ guint32 cpms_tries;
+ guint cpms_timeout;
} MMModemZtePrivate;
MMModem *
@@ -51,35 +55,278 @@ mm_modem_zte_new (const char *device,
}
/*****************************************************************************/
-/* Modem class override functions */
+
+static void
+zte_access_tech_changed (MMAtSerialPort *port,
+ GMatchInfo *info,
+ gpointer user_data)
+{
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ char *str;
+
+ str = g_match_info_fetch (info, 1);
+ if (str)
+ act = mm_gsm_string_to_access_tech (str);
+ g_free (str);
+
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (user_data), act);
+}
+
/*****************************************************************************/
static void
-init_modem_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+get_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GRegex *r = NULL;
+ GMatchInfo *match_info;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ r = g_regex_new ("+ZSNT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL);
+ if (!r) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the allowed mode response");
+ goto done;
+ }
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
+ if (g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) {
+ MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ char *str;
+ int cm_mode = -1, pref_acq = -1;
+
+ str = g_match_info_fetch (match_info, 1);
+ cm_mode = atoi (str);
+ g_free (str);
+
+ str = g_match_info_fetch (match_info, 3);
+ pref_acq = atoi (str);
+ g_free (str);
+
+ g_match_info_free (match_info);
+
+ if (cm_mode < 0 || cm_mode > 2 || pref_acq < 0 || pref_acq > 2) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the allowed mode response: '%s'",
+ response->str);
+ goto done;
+ }
+
+ if (cm_mode == 0) { /* Both 2G and 3G allowed */
+ if (pref_acq == 0)
+ mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ else if (pref_acq == 1)
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ else if (pref_acq == 2)
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ } else if (cm_mode == 1) /* GSM only */
+ mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ else if (cm_mode == 2) /* WCDMA only */
+ mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL);
+ }
+
+done:
+ if (r)
+ g_regex_unref (r);
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_allowed_mode (MMGenericGsm *gsm,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "AT+ZSNT?", 3, get_allowed_mode_done, info);
}
static void
-pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+set_allowed_mode_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMSerialPort *primary;
- if (error) {
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info);
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMGenericGsm *gsm,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+ char *command;
+ int cm_mode = 0, pref_acq = 0;
+
+ info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (gsm, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
return;
}
- /* Finish the initialization */
- primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
- g_assert (primary);
- mm_serial_port_queue_command (primary, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1;", 10, init_modem_done, info);
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ cm_mode = 1;
+ pref_acq = 0;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ cm_mode = 2;
+ pref_acq = 0;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ cm_mode = 0;
+ pref_acq = 1;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ cm_mode = 0;
+ pref_acq = 2;
+ break;
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ default:
+ break;
+ }
+
+ command = g_strdup_printf ("AT+ZSNT=%d,0,%d", cm_mode, pref_acq);
+ mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info);
+ g_free (command);
+}
+
+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 (error)
+ info->error = g_error_copy (error);
+ else {
+ /* Sample response from an MF626:
+ * +ZPAS: "GPRS/EDGE","CS_ONLY"
+ */
+ p = mm_strip_tag (response->str, "+ZPAS:");
+ act = mm_gsm_string_to_access_tech (p);
+ }
+
+ 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, "+ZPAS?", 3, get_act_request_done, info);
+}
+
+/*****************************************************************************/
+/* Modem class override functions */
+/*****************************************************************************/
+
+static void cpms_try_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static gboolean
+cpms_timeout_cb (gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModem *modem = info->modem;
+ MMAtSerialPort *primary;
+
+ if (modem) {
+ MM_MODEM_ZTE_GET_PRIVATE (modem)->cpms_timeout = 0;
+ primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY);
+ g_assert (primary);
+ mm_at_serial_port_queue_command (primary, "+CPMS?", 10, cpms_try_done, info);
+ }
+ return FALSE;
+}
+
+static void
+cpms_try_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (info->modem);
+
+ if (error && g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_SIM_BUSY)) {
+ if (priv->cpms_tries++ < 4) {
+ if (priv->cpms_timeout)
+ g_source_remove (priv->cpms_timeout);
+
+ /* Have to try a few times; sometimes the SIM is busy */
+ priv->cpms_timeout = g_timeout_add_seconds (2, cpms_timeout_cb, info);
+ return;
+ } else {
+ /* oh well, proceed... */
+ error = NULL;
+ }
+ }
+
+ mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
+}
+
+static void
+init_modem_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ /* Attempt to disable floods of "+ZUSIMR:2" unsolicited responses that
+ * eventually fill up the device's buffers and make it crash. Normally
+ * done during probing, but if the device has a PIN enabled it won't
+ * accept the +CPMS? during the probe and we have to do it here.
+ */
+ mm_at_serial_port_queue_command (port, "+CPMS?", 10, cpms_try_done, info);
}
static void enable_flash_done (MMSerialPort *port,
@@ -87,7 +334,7 @@ static void enable_flash_done (MMSerialPort *port,
gpointer user_data);
static void
-pre_init_done (MMSerialPort *port,
+pre_init_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -98,15 +345,14 @@ pre_init_done (MMSerialPort *port,
if (error) {
/* Retry the init string one more time; the modem sometimes throws it away */
if ( !priv->init_retried
- && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) {
+ && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
priv->init_retried = TRUE;
- enable_flash_done (port, NULL, user_data);
+ enable_flash_done (MM_SERIAL_PORT (port), NULL, user_data);
} else
mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
} else {
- /* Now check the PIN explicitly, zte doesn't seem to report
- that it needs it otherwise */
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info);
+ /* Finish the initialization */
+ mm_at_serial_port_queue_command (port, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1;", 10, init_modem_done, info);
}
}
@@ -118,7 +364,7 @@ enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
if (error)
mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info);
else
- mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "E0 V1", 3, pre_init_done, user_data);
}
static void
@@ -126,15 +372,15 @@ do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data)
{
MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem);
MMCallbackInfo *info;
- MMSerialPort *primary;
+ MMAtSerialPort *primary;
priv->init_retried = FALSE;
- primary = mm_generic_gsm_get_port (modem, MM_PORT_TYPE_PRIMARY);
+ primary = mm_generic_gsm_get_at_port (modem, MM_PORT_TYPE_PRIMARY);
g_assert (primary);
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_flash (primary, 100, enable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (primary), 100, FALSE, enable_flash_done, info);
}
static void
@@ -165,42 +411,41 @@ grab_port (MMModem *modem,
MMPort *port = NULL;
if (suggested_type == MM_PORT_TYPE_UNKNOWN) {
- if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY))
+ if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY))
ptype = MM_PORT_TYPE_PRIMARY;
- else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY))
+ else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY))
ptype = MM_PORT_TYPE_SECONDARY;
} else
ptype = suggested_type;
port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error);
- if (port && MM_IS_SERIAL_PORT (port)) {
+ if (port && MM_IS_AT_SERIAL_PORT (port)) {
GRegex *regex;
- mm_generic_gsm_set_unsolicited_registration (gsm, TRUE);
g_object_set (port, MM_PORT_CARRIER_DETECT, FALSE, NULL);
regex = g_regex_new ("\\r\\n\\+ZUSIMR:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Unsolicted operator display */
regex = g_regex_new ("\\r\\n\\+ZDONR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* Current network and service domain */
- regex = g_regex_new ("\\r\\n\\+ZPASR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ regex = g_regex_new ("\\r\\n\\+ZPASR:\\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, zte_access_tech_changed, modem, NULL);
g_regex_unref (regex);
/* SIM request to Build Main Menu */
regex = g_regex_new ("\\r\\n\\+ZPSTM: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
/* SIM request to Rebuild Main Menu */
regex = g_regex_new ("\\r\\n\\+ZEND\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL);
g_regex_unref (regex);
}
@@ -222,6 +467,16 @@ mm_modem_zte_init (MMModemZte *self)
}
static void
+dispose (GObject *object)
+{
+ MMModemZte *self = MM_MODEM_ZTE (object);
+ MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self);
+
+ if (priv->cpms_timeout)
+ g_source_remove (priv->cpms_timeout);
+}
+
+static void
mm_modem_zte_class_init (MMModemZteClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -230,6 +485,10 @@ mm_modem_zte_class_init (MMModemZteClass *klass)
mm_modem_zte_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (object_class, sizeof (MMModemZtePrivate));
+ object_class->dispose = dispose;
gsm_class->do_enable = do_enable;
+ gsm_class->set_allowed_mode = set_allowed_mode;
+ gsm_class->get_allowed_mode = get_allowed_mode;
+ gsm_class->get_access_technology = get_access_technology;
}
diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c
index e451714..94f4f10 100644
--- a/plugins/mm-plugin-anydata.c
+++ b/plugins/mm-plugin-anydata.c
@@ -42,9 +42,11 @@ mm_plugin_create (void)
static guint32
get_level_for_capabilities (guint32 capabilities)
{
- /* Only CDMA for now */
+ /* Only CDMA and QCDM for now */
if (capabilities & CAP_CDMA)
return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 10;
return 0;
}
@@ -104,7 +106,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
@@ -118,14 +120,6 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
@@ -135,6 +129,7 @@ grab_port (MMPluginBase *base,
return NULL;
}
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & CAP_CDMA) {
modem = mm_modem_anydata_cdma_new (sysfs_path,
@@ -150,12 +145,15 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & CAP_CDMA) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ MMPortType ptype = MM_PORT_TYPE_UNKNOWN;
+
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c
index e5e2ade..cdf2c66 100644
--- a/plugins/mm-plugin-generic.c
+++ b/plugins/mm-plugin-generic.c
@@ -59,6 +59,8 @@ get_level_for_capabilities (guint32 capabilities)
return 5;
if (capabilities & CAP_CDMA)
return 5;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 5;
return 0;
}
@@ -106,7 +108,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path, *driver;
guint32 caps;
@@ -131,15 +133,8 @@ grab_port (MMPluginBase *base,
}
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_generic_gsm_new (sysfs_path,
@@ -159,12 +154,15 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ MMPortType ptype = MM_PORT_TYPE_UNKNOWN;
+
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c
index 77da965..fbe3878 100644
--- a/plugins/mm-plugin-gobi.c
+++ b/plugins/mm-plugin-gobi.c
@@ -102,7 +102,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *sysfs_path;
guint32 caps;
@@ -110,18 +110,11 @@ grab_port (MMPluginBase *base,
port = mm_plugin_base_supports_task_get_port (task);
g_assert (port);
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_gobi_gsm_new (sysfs_path,
@@ -141,12 +134,10 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c
index 8493c9c..dc0a8fc 100644
--- a/plugins/mm-plugin-hso.c
+++ b/plugins/mm-plugin-hso.c
@@ -100,7 +100,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *sysfs_path;
char *devfile;
@@ -131,18 +131,11 @@ grab_port (MMPluginBase *base,
}
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- goto out;
- }
-
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net"))
goto out;
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
modem = mm_modem_hso_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c
index ad799f0..2993689 100644
--- a/plugins/mm-plugin-huawei.c
+++ b/plugins/mm-plugin-huawei.c
@@ -26,6 +26,7 @@
#include "mm-modem-huawei-gsm.h"
#include "mm-modem-huawei-cdma.h"
#include "mm-serial-parsers.h"
+#include "mm-at-serial-port.h"
G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE)
@@ -54,6 +55,8 @@ get_level_for_capabilities (guint32 capabilities)
return 10;
if (capabilities & CAP_CDMA)
return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 10;
return 0;
}
@@ -69,9 +72,14 @@ probe_result (MMPluginBase *base,
#define TAG_SUPPORTS_INFO "huawei-supports-info"
typedef struct {
- MMSerialPort *serial;
+ MMAtSerialPort *serial;
guint id;
- gboolean secondary;
+ MMPortType ptype;
+ /* Whether or not there's already a detected modem that "owns" this port,
+ * in which case we'll claim it, but if no capabilities are detected it'll
+ * just be ignored.
+ */
+ gboolean parent_modem;
} HuaweiSupportsInfo;
static void
@@ -100,13 +108,13 @@ probe_secondary_supported (gpointer user_data)
info->serial = NULL;
/* Yay, supported, we got an unsolicited message */
- info->secondary = TRUE;
+ info->ptype = MM_PORT_TYPE_SECONDARY;
mm_plugin_base_supports_task_complete (task, 10);
return FALSE;
}
static void
-probe_secondary_handle_msg (MMSerialPort *port,
+probe_secondary_handle_msg (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
@@ -123,24 +131,30 @@ probe_secondary_timeout (gpointer user_data)
{
MMPluginBaseSupportsTask *task = user_data;
HuaweiSupportsInfo *info;
+ guint level = 0;
info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO);
info->id = 0;
g_object_unref (info->serial);
info->serial = NULL;
- /* Not supported by this plugin */
- mm_plugin_base_supports_task_complete (task, 0);
+ /* Supported, but ignored if this port's parent device is already a modem */
+ if (info->parent_modem) {
+ info->ptype = MM_PORT_TYPE_IGNORED;
+ level = 10;
+ }
+
+ mm_plugin_base_supports_task_complete (task, level);
return FALSE;
}
static void
-add_regex (MMSerialPort *port, const char *match, gpointer user_data)
+add_regex (MMAtSerialPort *port, const char *match, gpointer user_data)
{
GRegex *regex;
regex = g_regex_new (match, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (port, regex, probe_secondary_handle_msg, user_data, NULL);
+ mm_at_serial_port_add_unsolicited_msg_handler (port, regex, probe_secondary_handle_msg, user_data, NULL);
g_regex_unref (regex);
}
@@ -207,14 +221,15 @@ supports_port (MMPluginBase *base,
/* Listen for Huawei-specific unsolicited messages */
info = g_malloc0 (sizeof (HuaweiSupportsInfo));
+ info->parent_modem = !!existing;
- info->serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ info->serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
g_object_set (G_OBJECT (info->serial), MM_PORT_CARRIER_DETECT, FALSE, NULL);
- mm_serial_port_set_response_parser (info->serial,
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ mm_at_serial_port_set_response_parser (info->serial,
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
add_regex (info->serial, "\\r\\n\\^RSSI:(\\d+)\\r\\n", task);
add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task);
@@ -222,12 +237,12 @@ supports_port (MMPluginBase *base,
add_regex (info->serial, "\\r\\n\\^BOOT:.+\\r\\n", task);
add_regex (info->serial, "\\r\\r\\^BOOT:.+\\r\\r", task);
- info->id = g_timeout_add (5000, probe_secondary_timeout, task);
+ info->id = g_timeout_add_seconds (7, probe_secondary_timeout, task);
g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO,
info, huawei_supports_info_destroy);
- if (!mm_serial_port_open (info->serial, &error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (info->serial), &error)) {
g_warning ("%s: (Huawei) %s: couldn't open serial port: (%d) %s",
__func__, name,
error ? error->code : -1,
@@ -249,7 +264,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
@@ -264,14 +279,6 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
@@ -281,18 +288,12 @@ grab_port (MMPluginBase *base,
}
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
- if (product == 0x1001) {
- /* This modem is handled by generic GSM driver */
- modem = mm_generic_gsm_new (sysfs_path,
- mm_plugin_base_supports_task_get_driver (task),
- mm_plugin_get_name (MM_PLUGIN (base)));
- } else {
- modem = mm_modem_huawei_gsm_new (sysfs_path,
- mm_plugin_base_supports_task_get_driver (task),
- mm_plugin_get_name (MM_PLUGIN (base)));
- }
+ modem = mm_modem_huawei_gsm_new (sysfs_path,
+ mm_plugin_base_supports_task_get_driver (task),
+ mm_plugin_get_name (MM_PLUGIN (base)));
} else if (caps & CAP_CDMA) {
modem = mm_modem_huawei_cdma_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
@@ -312,8 +313,10 @@ grab_port (MMPluginBase *base,
MMPortType ptype = MM_PORT_TYPE_UNKNOWN;
info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO);
- if (info && info->secondary && (product != 0x1001))
- ptype = MM_PORT_TYPE_SECONDARY;
+ if (info)
+ ptype = info->ptype;
+ else if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
modem = existing;
if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c
index 5c19b13..dbbe186 100644
--- a/plugins/mm-plugin-longcheer.c
+++ b/plugins/mm-plugin-longcheer.c
@@ -11,12 +11,13 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include <string.h>
#include <gmodule.h>
#include "mm-plugin-longcheer.h"
+#include "mm-modem-longcheer-gsm.h"
#include "mm-generic-gsm.h"
#include "mm-generic-cdma.h"
@@ -47,6 +48,8 @@ get_level_for_capabilities (guint32 capabilities)
return 10;
if (capabilities & CAP_CDMA)
return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 10;
return 0;
}
@@ -80,7 +83,8 @@ supports_port (MMPluginBase *base,
if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL))
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- if (vendor != 0x1c9e)
+ /* Longcheer and TAMobile */
+ if (vendor != 0x1c9e && vendor != 0x1bbb)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) {
@@ -105,7 +109,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *sysfs_path;
guint32 caps;
@@ -131,23 +135,16 @@ grab_port (MMPluginBase *base,
&& g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_TAGGED"))
ptype = MM_PORT_TYPE_IGNORED;
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
- modem = mm_generic_gsm_new (sysfs_path,
- mm_plugin_base_supports_task_get_driver (task),
- mm_plugin_get_name (MM_PLUGIN (base)));
+ modem = mm_modem_longcheer_gsm_new (sysfs_path,
+ mm_plugin_base_supports_task_get_driver (task),
+ mm_plugin_get_name (MM_PLUGIN (base)));
} else if (caps & CAP_CDMA) {
modem = mm_generic_cdma_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
@@ -162,12 +159,13 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c
index a380f98..5554d84 100644
--- a/plugins/mm-plugin-mbm.c
+++ b/plugins/mm-plugin-mbm.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <string.h>
@@ -66,9 +62,12 @@ supports_port (MMPluginBase *base,
MMModem *existing,
MMPluginBaseSupportsTask *task)
{
+ GUdevClient *client;
+ const char *sys[] = { "tty", "net", NULL };
GUdevDevice *port, *physdev;
guint32 cached = 0, level;
- const char *driver, *subsys;
+ const char *driver, *subsys, *physdev_path;
+ gboolean is_mbm;
/* Can't do anything with non-serial ports */
port = mm_plugin_base_supports_task_get_port (task);
@@ -83,9 +82,23 @@ supports_port (MMPluginBase *base,
if (!driver)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- physdev = mm_plugin_base_supports_task_get_physdev (task);
+ client = g_udev_client_new (sys);
+ if (!client) {
+ g_warning ("mbm: could not get udev client.");
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+ }
+
+ /* Look up the port's physical device and see if this port is really an
+ * 'mbm' modem, since we have no other way of telling.
+ */
+ physdev_path = mm_plugin_base_supports_task_get_physdev_path (task);
+ physdev = g_udev_client_query_by_sysfs_path (client, physdev_path);
g_assert (physdev);
- if (!g_udev_device_get_property_as_boolean (physdev, "ID_MM_ERICSSON_MBM"))
+
+ is_mbm = g_udev_device_get_property_as_boolean (physdev, "ID_MM_ERICSSON_MBM");
+ g_object_unref (client);
+
+ if (!is_mbm)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
if (!strcmp (subsys, "net")) {
@@ -115,7 +128,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *sysfs_path;
guint32 caps;
@@ -123,14 +136,6 @@ grab_port (MMPluginBase *base,
port = mm_plugin_base_supports_task_get_port (task);
g_assert (port);
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
@@ -138,6 +143,7 @@ grab_port (MMPluginBase *base,
if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net"))
return NULL;
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
modem = mm_modem_mbm_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
diff --git a/plugins/mm-plugin-mbm.h b/plugins/mm-plugin-mbm.h
index c0e73b5..c478f11 100644
--- a/plugins/mm-plugin-mbm.h
+++ b/plugins/mm-plugin-mbm.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#ifndef MM_PLUGIN_MBM_H
diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c
index 5b32a1e..d798af4 100644
--- a/plugins/mm-plugin-moto-c.c
+++ b/plugins/mm-plugin-moto-c.c
@@ -37,18 +37,21 @@ mm_plugin_create (void)
/*****************************************************************************/
+static guint32
+get_level_for_capabilities (guint32 capabilities)
+{
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM)
+ return 10;
+ return 0;
+}
+
static void
probe_result (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
guint32 capabilities,
gpointer user_data)
{
- guint32 level = 0;
-
- if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM)
- level = 10;
-
- mm_plugin_base_supports_task_complete (task, level);
+ mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities));
}
static MMPluginSupportsResult
@@ -58,7 +61,7 @@ supports_port (MMPluginBase *base,
{
GUdevDevice *port;
const char *tmp;
- guint32 cached = 0;
+ guint32 cached = 0, level;
/* Can't do anything with non-serial ports */
port = mm_plugin_base_supports_task_get_port (task);
@@ -78,7 +81,8 @@ supports_port (MMPluginBase *base,
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) {
- if (cached & MM_PLUGIN_BASE_PORT_CAP_GSM) {
+ level = get_level_for_capabilities (cached);
+ if (level) {
mm_plugin_base_supports_task_complete (task, 10);
return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
}
@@ -98,7 +102,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
@@ -111,17 +115,10 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
modem = mm_modem_moto_c_gsm_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c
index e088323..2d0d6af 100644
--- a/plugins/mm-plugin-nokia.c
+++ b/plugins/mm-plugin-nokia.c
@@ -105,7 +105,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
@@ -119,18 +119,11 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_nokia_new (sysfs_path,
@@ -150,12 +143,10 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c
index 48ff7ec..a968836 100644
--- a/plugins/mm-plugin-novatel.c
+++ b/plugins/mm-plugin-novatel.c
@@ -18,7 +18,7 @@
#include <gmodule.h>
#include "mm-plugin-novatel.h"
#include "mm-modem-novatel-gsm.h"
-#include "mm-generic-cdma.h"
+#include "mm-modem-novatel-cdma.h"
G_DEFINE_TYPE (MMPluginNovatel, mm_plugin_novatel, MM_TYPE_PLUGIN_BASE)
@@ -47,6 +47,8 @@ get_level_for_capabilities (guint32 capabilities)
return 10;
if (capabilities & CAP_CDMA)
return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 10;
return 0;
}
@@ -109,7 +111,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
@@ -123,29 +125,22 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_novatel_gsm_new (sysfs_path,
mm_plugin_base_supports_task_get_driver (task),
mm_plugin_get_name (MM_PLUGIN (base)));
} else if (caps & CAP_CDMA) {
- modem = mm_generic_cdma_new (sysfs_path,
- mm_plugin_base_supports_task_get_driver (task),
- mm_plugin_get_name (MM_PLUGIN (base)),
- !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856),
- !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A));
+ modem = mm_modem_novatel_cdma_new (sysfs_path,
+ mm_plugin_base_supports_task_get_driver (task),
+ mm_plugin_get_name (MM_PLUGIN (base)),
+ !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856),
+ !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A));
}
if (modem) {
@@ -154,12 +149,15 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ MMPortType ptype = MM_PORT_TYPE_UNKNOWN;
+
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c
index d4c402d..101f9bd 100644
--- a/plugins/mm-plugin-option.c
+++ b/plugins/mm-plugin-option.c
@@ -101,7 +101,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
@@ -117,14 +117,6 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
@@ -138,6 +130,7 @@ grab_port (MMPluginBase *base,
ptype = MM_PORT_TYPE_PRIMARY;
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_option_new (sysfs_path,
@@ -151,12 +144,13 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c
index 637f46d..8ace653 100644
--- a/plugins/mm-plugin-sierra.c
+++ b/plugins/mm-plugin-sierra.c
@@ -123,7 +123,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *devfile, *sysfs_path;
guint32 caps;
@@ -138,14 +138,6 @@ grab_port (MMPluginBase *base,
return NULL;
}
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
@@ -154,6 +146,7 @@ grab_port (MMPluginBase *base,
ptype = MM_PORT_TYPE_SECONDARY;
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if ((caps & MM_PLUGIN_BASE_PORT_CAP_GSM) || (ptype != MM_PORT_TYPE_UNKNOWN)) {
modem = mm_modem_sierra_gsm_new (sysfs_path,
@@ -173,13 +166,10 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if ( (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA))
- || (ptype != MM_PORT_TYPE_UNKNOWN)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps) || (ptype != MM_PORT_TYPE_UNKNOWN)) {
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/plugins/mm-plugin-simtech.c b/plugins/mm-plugin-simtech.c
new file mode 100644
index 0000000..3c44873
--- /dev/null
+++ b/plugins/mm-plugin-simtech.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <gmodule.h>
+#include "mm-plugin-simtech.h"
+#include "mm-modem-simtech-gsm.h"
+#include "mm-generic-gsm.h"
+#include "mm-generic-cdma.h"
+
+G_DEFINE_TYPE (MMPluginSimtech, mm_plugin_simtech, MM_TYPE_PLUGIN_BASE)
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_SIMTECH,
+ MM_PLUGIN_BASE_NAME, "SimTech",
+ NULL));
+}
+
+/*****************************************************************************/
+
+#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \
+ MM_PLUGIN_BASE_PORT_CAP_IS707_P | \
+ MM_PLUGIN_BASE_PORT_CAP_IS856 | \
+ MM_PLUGIN_BASE_PORT_CAP_IS856_A)
+
+static guint32
+get_level_for_capabilities (guint32 capabilities)
+{
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM)
+ return 10;
+ if (capabilities & CAP_CDMA)
+ return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 10;
+ return 0;
+}
+
+static void
+probe_result (MMPluginBase *base,
+ MMPluginBaseSupportsTask *task,
+ guint32 capabilities,
+ gpointer user_data)
+{
+ mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities));
+}
+
+static MMPluginSupportsResult
+supports_port (MMPluginBase *base,
+ MMModem *existing,
+ MMPluginBaseSupportsTask *task)
+{
+ GUdevDevice *port;
+ guint32 cached = 0, level;
+ guint16 vendor = 0;
+ const char *subsys, *name;
+
+ /* Can't do anything with non-serial ports */
+ port = mm_plugin_base_supports_task_get_port (task);
+ if (strcmp (g_udev_device_get_subsystem (port), "tty"))
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+
+ subsys = g_udev_device_get_subsystem (port);
+ name = g_udev_device_get_name (port);
+
+ if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL))
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+
+ /* A-Link (for now) */
+ if (vendor != 0x1e0e)
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+
+ if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) {
+ level = get_level_for_capabilities (cached);
+ if (level) {
+ mm_plugin_base_supports_task_complete (task, level);
+ return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
+ }
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+ }
+
+ /* Otherwise kick off a probe */
+ if (mm_plugin_base_probe_port (base, task, NULL))
+ return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
+
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+}
+
+static MMModem *
+grab_port (MMPluginBase *base,
+ MMModem *existing,
+ MMPluginBaseSupportsTask *task,
+ GError **error)
+{
+ GUdevDevice *port = NULL;
+ MMModem *modem = NULL;
+ const char *name, *subsys, *sysfs_path;
+ guint32 caps;
+ MMPortType ptype = MM_PORT_TYPE_UNKNOWN;
+
+ port = mm_plugin_base_supports_task_get_port (task);
+ g_assert (port);
+
+ /* Look for port type hints; just probing can't distinguish which port should
+ * be the data/primary port on these devices. We have to tag them based on
+ * what the Windows .INF files say the port layout should be.
+ */
+ if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_MODEM"))
+ ptype = MM_PORT_TYPE_PRIMARY;
+ else if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_AUX"))
+ ptype = MM_PORT_TYPE_SECONDARY;
+
+ /* If the device was tagged by the udev rules, then ignore any other ports
+ * to guard against race conditions if a device just happens to show up
+ * with more than two AT-capable ports.
+ */
+ if ( (ptype == MM_PORT_TYPE_UNKNOWN)
+ && g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_TAGGED"))
+ ptype = MM_PORT_TYPE_IGNORED;
+
+ subsys = g_udev_device_get_subsystem (port);
+ name = g_udev_device_get_name (port);
+
+ caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
+ if (!existing) {
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
+ modem = mm_modem_simtech_gsm_new (sysfs_path,
+ mm_plugin_base_supports_task_get_driver (task),
+ mm_plugin_get_name (MM_PLUGIN (base)));
+ } else if (caps & CAP_CDMA) {
+ modem = mm_generic_cdma_new (sysfs_path,
+ mm_plugin_base_supports_task_get_driver (task),
+ mm_plugin_get_name (MM_PLUGIN (base)),
+ !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856),
+ !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A));
+ }
+
+ if (modem) {
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) {
+ g_object_unref (modem);
+ return NULL;
+ }
+ }
+ } else if (get_level_for_capabilities (caps)) {
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
+ }
+
+ return modem;
+}
+
+/*****************************************************************************/
+
+static void
+mm_plugin_simtech_init (MMPluginSimtech *self)
+{
+ g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL);
+}
+
+static void
+mm_plugin_simtech_class_init (MMPluginSimtechClass *klass)
+{
+ MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass);
+
+ pb_class->supports_port = supports_port;
+ pb_class->grab_port = grab_port;
+}
diff --git a/plugins/mm-plugin-simtech.h b/plugins/mm-plugin-simtech.h
new file mode 100644
index 0000000..e316056
--- /dev/null
+++ b/plugins/mm-plugin-simtech.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_PLUGIN_SIMTECH_H
+#define MM_PLUGIN_SIMTECH_H
+
+#include "mm-plugin-base.h"
+
+#define MM_TYPE_PLUGIN_SIMTECH (mm_plugin_simtech_get_type ())
+#define MM_PLUGIN_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIMTECH, MMPluginSimtech))
+#define MM_PLUGIN_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIMTECH, MMPluginSimtechClass))
+#define MM_IS_PLUGIN_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIMTECH))
+#define MM_IS_PLUGIN_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIMTECH))
+#define MM_PLUGIN_SIMTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIMTECH, MMPluginSimtechClass))
+
+typedef struct {
+ MMPluginBase parent;
+} MMPluginSimtech;
+
+typedef struct {
+ MMPluginBaseClass parent;
+} MMPluginSimtechClass;
+
+GType mm_plugin_simtech_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_SIMTECH_H */
diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c
index 101fb46..e943bbf 100644
--- a/plugins/mm-plugin-zte.c
+++ b/plugins/mm-plugin-zte.c
@@ -47,6 +47,8 @@ get_level_for_capabilities (guint32 capabilities)
return 10;
if (capabilities & CAP_CDMA)
return 10;
+ if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ return 10;
return 0;
}
@@ -100,7 +102,7 @@ supports_port (MMPluginBase *base,
* 1235f71b20c92cded4abd976ccc5010649aae1a0 and
* f38ad328acfdc6ce29dd1380602c546b064161ae for more details.
*/
- mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, TRUE);
+ mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, FALSE);
if (mm_plugin_base_probe_port (base, task, NULL))
return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS;
@@ -114,7 +116,7 @@ grab_port (MMPluginBase *base,
MMPluginBaseSupportsTask *task,
GError **error)
{
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
MMModem *modem = NULL;
const char *name, *subsys, *sysfs_path;
guint32 caps;
@@ -129,18 +131,11 @@ grab_port (MMPluginBase *base,
else if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_AUX"))
ptype = MM_PORT_TYPE_SECONDARY;
- physdev = mm_plugin_base_supports_task_get_physdev (task);
- g_assert (physdev);
- sysfs_path = g_udev_device_get_sysfs_path (physdev);
- if (!sysfs_path) {
- g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
- return NULL;
- }
-
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
caps = mm_plugin_base_supports_task_get_probed_capabilities (task);
+ sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task);
if (!existing) {
if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) {
modem = mm_modem_zte_new (sysfs_path,
@@ -160,12 +155,13 @@ grab_port (MMPluginBase *base,
return NULL;
}
}
- } else {
- if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) {
- modem = existing;
- if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
- return NULL;
- }
+ } else if (get_level_for_capabilities (caps)) {
+ if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
+ ptype = MM_PORT_TYPE_QCDM;
+
+ modem = existing;
+ if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error))
+ return NULL;
}
return modem;
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/po/LINGUAS
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..f97552d
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,5 @@
+[encoding: UTF-8]
+# List of source files containing translatable strings.
+# Please keep this file sorted alphabetically.
+policy/org.freedesktop.modem-manager.policy.in
+
diff --git a/policy/Makefile.am b/policy/Makefile.am
new file mode 100644
index 0000000..81a817b
--- /dev/null
+++ b/policy/Makefile.am
@@ -0,0 +1,15 @@
+icondir=${datadir}/icons/hicolor/22x22/apps
+icon_DATA = modem-manager.png
+
+polkit_policydir = $(datadir)/polkit-1/actions
+
+dist_polkit_policy_in_files = org.freedesktop.modem-manager.policy.in
+dist_polkit_policy_DATA = $(dist_polkit_policy_in_files:.policy.in=.policy)
+
+@INTLTOOL_POLICY_RULE@
+
+clean-local :
+ rm -f *~
+
+EXTRA_DIST = $(dist_polkit_policy_in_files) $(icon_DATA)
+
diff --git a/policy/modem-manager.png b/policy/modem-manager.png
new file mode 100644
index 0000000..386abd5
--- /dev/null
+++ b/policy/modem-manager.png
Binary files differ
diff --git a/policy/org.freedesktop.modem-manager.policy.in b/policy/org.freedesktop.modem-manager.policy.in
new file mode 100644
index 0000000..8719936
--- /dev/null
+++ b/policy/org.freedesktop.modem-manager.policy.in
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+ <vendor>ModemManager</vendor>
+ <vendor_url>http://www.freedesktop.org/wiki/ModemManager</vendor_url>
+ <icon_name>modem-manager</icon_name>
+
+ <action id="org.freedesktop.ModemManager.Device.Control">
+ <_description>Unlock and control a mobile broadband device</_description>
+ <_message>System policy prevents unlocking or controlling the mobile broadband device.</_message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.ModemManager.Device.Info">
+ <_description>Request mobile broadband device identifying information</_description>
+ <_message>System policy prevents requesting identifying information from the mobile broadband device.</_message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.ModemManager.Contacts">
+ <_description>Add, modify, and delete mobile broadband contacts</_description>
+ <_message>System policy prevents adding, modifying, or deleteing this device's contacts.</_message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.ModemManager.SMS">
+ <_description>Send, save, modify, and delete text messages</_description>
+ <_message>System policy prevents sending or maniuplating this device's text messages.</_message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.ModemManager.Location">
+ <_description>Enable and view geographic location and positioning information</_description>
+ <_message>System policy prevents enabling or viewing geographic location information.</_message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
diff --git a/src/77-mm-pcmcia-device-blacklist.rules b/src/77-mm-pcmcia-device-blacklist.rules
new file mode 100644
index 0000000..76259a2
--- /dev/null
+++ b/src/77-mm-pcmcia-device-blacklist.rules
@@ -0,0 +1,10 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_pcmcia_device_blacklist_end"
+SUBSYSTEM!="pcmcia", GOTO="mm_pcmcia_device_blacklist_end"
+
+# Gemplus Serial Port smartcard adapter
+ATTRS{prod_id1}=="Gemplus", ATTRS{prod_id2}=="SerialPort", ATTRS{prod_id3}=="GemPC Card", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+LABEL="mm_pcmcia_device_blacklist_end"
+
diff --git a/src/77-mm-platform-serial-whitelist.rules b/src/77-mm-platform-serial-whitelist.rules
new file mode 100644
index 0000000..b62d0a6
--- /dev/null
+++ b/src/77-mm-platform-serial-whitelist.rules
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_platform_device_whitelist_end"
+SUBSYSTEM!="platform", GOTO="mm_platform_device_whitelist_end"
+
+# Be careful here since many devices connected to platform drivers on PCs
+# are legacy devices that won't like probing. But often on embedded
+# systems serial ports are provided by platform devices.
+
+# Allow atmel_usart
+DRIVERS=="atmel_usart", ENV{ID_MM_PLATFORM_DRIVER_PROBE}="1"
+
+LABEL="mm_platform_device_whitelist_end"
+
diff --git a/src/77-mm-usb-device-blacklist.rules b/src/77-mm-usb-device-blacklist.rules
new file mode 100644
index 0000000..78a6770
--- /dev/null
+++ b/src/77-mm-usb-device-blacklist.rules
@@ -0,0 +1,66 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_usb_device_blacklist_end"
+SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
+ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
+
+# APC UPS devices
+ATTRS{idVendor}=="051d", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Sweex 1000VA
+ATTRS{idVendor}=="0925", ATTRS{idProduct}=="1234", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Agiler UPS
+ATTRS{idVendor}=="05b8", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Krauler UP-M500VA
+ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Ablerex 625L USB
+ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Belkin F6C1200-UNV
+ATTRS{idVendor}=="0665", ATTRS{idProduct}=="5161", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various Liebert and Phoenixtec Power devices
+ATTRS{idVendor}=="06da", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Unitek Alpha 1200Sx
+ATTRS{idVendor}=="0f03", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various Tripplite devices
+ATTRS{idVendor}=="09ae", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various MGE Office Protection Systems devices
+ATTRS{idVendor}=="0463", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="0463", ATTRS{idProduct}=="ffff", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# CyberPower 900AVR/BC900D
+ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0005", ENV{ID_MM_DEVICE_IGNORE}="1"
+# CyberPower CP1200AVR/BC1200D
+ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0501", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Various Belkin devices
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0980", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0900", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0910", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0912", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0551", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0751", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0375", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="050d", ATTRS{idProduct}=="1100", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# HP R/T 2200 INTL (like SMART2200RMXL2U)
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f0a", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Powerware devices
+ATTRS{idVendor}=="0592", ATTRS{idProduct}=="0002", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Palm Treo 700/900/etc
+# Shouldn't be probed themselves, but you can install programs like
+# "MobileStream USB Modem" which changes the USB PID of the device to something
+# that isn't blacklisted.
+ATTRS{idVendor}=="0830", ATTRS{idProduct}=="0061", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+LABEL="mm_usb_device_blacklist_end"
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 9209b55..2061ae8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,14 @@
SUBDIRS=. tests
+udevrulesdir = $(UDEV_BASE_DIR)/rules.d
+udevrules_DATA = \
+ 77-mm-usb-device-blacklist.rules \
+ 77-mm-pcmcia-device-blacklist.rules \
+ 77-mm-platform-serial-whitelist.rules
+
+EXTRA_DIST = \
+ $(udevrules_DATA)
+
noinst_LTLIBRARIES = libmodem-helpers.la
libmodem_helpers_la_CPPFLAGS = \
@@ -9,38 +18,76 @@ libmodem_helpers_la_SOURCES = \
mm-errors.c \
mm-errors.h \
mm-modem-helpers.c \
- mm-modem-helpers.h
+ mm-modem-helpers.h \
+ mm-charsets.c \
+ mm-charsets.h \
+ mm-utils.c \
+ mm-utils.h
sbin_PROGRAMS = modem-manager
modem_manager_CPPFLAGS = \
$(MM_CFLAGS) \
$(GUDEV_CFLAGS) \
+ -I$(top_srcdir) \
-I${top_builddir}/marshallers \
-DPLUGINDIR=\"$(pkglibdir)\"
+if WITH_POLKIT
+modem_manager_CPPFLAGS += $(POLKIT_CFLAGS)
+endif
+
modem_manager_LDADD = \
$(MM_LIBS) \
$(GUDEV_LIBS) \
$(top_builddir)/marshallers/libmarshallers.la \
+ $(top_builddir)/libqcdm/src/libqcdm.la \
$(builddir)/libmodem-helpers.la
+if WITH_POLKIT
+modem_manager_LDADD += $(POLKIT_LIBS)
+endif
+
+auth_sources = \
+ mm-auth-request.c \
+ mm-auth-request.h \
+ mm-auth-provider.h \
+ mm-auth-provider.c \
+ mm-auth-provider-factory.c
+
+if WITH_POLKIT
+auth_sources += \
+ mm-auth-request-polkit.c \
+ mm-auth-request-polkit.h \
+ mm-auth-provider-polkit.c \
+ mm-auth-provider-polkit.h
+endif
+
+loc_sources = \
+ mm-modem-location.c \
+ mm-modem-location.h
+
modem_manager_SOURCES = \
main.c \
mm-callback-info.c \
mm-callback-info.h \
+ $(auth_sources) \
mm-manager.c \
mm-manager.h \
mm-modem.c \
mm-modem.h \
mm-port.c \
mm-port.h \
- mm-modem-base.c \
- mm-modem-base.h \
mm-serial-port.c \
mm-serial-port.h \
+ mm-at-serial-port.c \
+ mm-at-serial-port.h \
+ mm-qcdm-serial-port.c \
+ mm-qcdm-serial-port.h \
mm-serial-parsers.c \
mm-serial-parsers.h \
+ mm-modem-base.c \
+ mm-modem-base.h \
mm-generic-cdma.c \
mm-generic-cdma.h \
mm-generic-gsm.c \
@@ -86,7 +133,6 @@ mm-modem-gsm-network-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-network.xm
mm-modem-gsm-sms-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-sms.xml
dbus-binding-tool --prefix=mm_modem_gsm_sms --mode=glib-server --output=$@ $<
-
BUILT_SOURCES = \
mm-manager-glue.h \
mm-modem-glue.h \
@@ -96,4 +142,15 @@ BUILT_SOURCES = \
mm-modem-gsm-network-glue.h \
mm-modem-gsm-sms-glue.h
+if WITH_LOCATION_API
+mm-modem-location-glue.h: $(top_srcdir)/introspection/mm-modem-location.xml
+ dbus-binding-tool --prefix=mm_modem_location --mode=glib-server --output=$@ $<
+
+modem_manager_SOURCES += $(loc_sources)
+
+BUILT_SOURCES += mm-modem-location-glue.h
+else
+EXTRA_DIST += $(loc_sources)
+endif
+
CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/main.c b/src/main.c
index 3669115..72fa6dc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,18 +11,22 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <signal.h>
#include <syslog.h>
#include <string.h>
+#include <unistd.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "mm-manager.h"
#include "mm-options.h"
-#define HAL_DBUS_SERVICE "org.freedesktop.Hal"
+#if !defined(MM_DIST_VERSION)
+# define MM_DIST_VERSION VERSION
+#endif
static GMainLoop *loop = NULL;
@@ -33,8 +37,11 @@ mm_signal_handler (int signo)
mm_options_set_debug (!mm_options_debug ());
else if (signo == SIGINT || signo == SIGTERM) {
g_message ("Caught signal %d, shutting down...", signo);
- g_main_loop_quit (loop);
- }
+ if (loop)
+ g_main_loop_quit (loop);
+ else
+ _exit (0);
+ }
}
static void
@@ -178,6 +185,8 @@ main (int argc, char *argv[])
if (!mm_options_debug ())
logging_setup ();
+ g_message ("ModemManager (version " MM_DIST_VERSION ") starting...");
+
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
if (!bus) {
g_warning ("Could not get the system bus. Make sure "
@@ -201,6 +210,16 @@ main (int argc, char *argv[])
g_signal_handler_disconnect (proxy, id);
+ mm_manager_shutdown (manager);
+
+ /* Wait for all modems to be removed */
+ while (mm_manager_num_modems (manager)) {
+ GMainContext *ctx = g_main_loop_get_context (loop);
+
+ g_main_context_iteration (ctx, FALSE);
+ g_usleep (50);
+ }
+
g_object_unref (manager);
g_object_unref (proxy);
dbus_g_connection_unref (bus);
diff --git a/src/mm-at-serial-port.c b/src/mm-at-serial-port.c
new file mode 100644
index 0000000..068450d
--- /dev/null
+++ b/src/mm-at-serial-port.c
@@ -0,0 +1,364 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#define _GNU_SOURCE /* for strcasestr() */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-at-serial-port.h"
+#include "mm-errors.h"
+#include "mm-options.h"
+
+G_DEFINE_TYPE (MMAtSerialPort, mm_at_serial_port, MM_TYPE_SERIAL_PORT)
+
+#define MM_AT_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortPrivate))
+
+typedef struct {
+ /* Response parser data */
+ MMAtSerialResponseParserFn response_parser_fn;
+ gpointer response_parser_user_data;
+ GDestroyNotify response_parser_notify;
+ GSList *unsolicited_msg_handlers;
+} MMAtSerialPortPrivate;
+
+
+/*****************************************************************************/
+
+void
+mm_at_serial_port_set_response_parser (MMAtSerialPort *self,
+ MMAtSerialResponseParserFn fn,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+
+ if (priv->response_parser_notify)
+ priv->response_parser_notify (priv->response_parser_user_data);
+
+ priv->response_parser_fn = fn;
+ priv->response_parser_user_data = user_data;
+ priv->response_parser_notify = notify;
+}
+
+static gboolean
+parse_response (MMSerialPort *port, GByteArray *response, GError **error)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+ gboolean found;
+ GString *string;
+
+ g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
+
+ /* Construct the string that AT-parsing functions expect */
+ string = g_string_sized_new (response->len + 1);
+ g_string_append_len (string, (const char *) response->data, response->len);
+
+ /* Parse it */
+ found = priv->response_parser_fn (priv->response_parser_user_data, string, error);
+
+ /* And copy it back into the response array after the parser has removed
+ * matches and cleaned it up.
+ */
+ if (response->len)
+ g_byte_array_remove_range (response, 0, response->len);
+ g_byte_array_append (response, (const guint8 *) string->str, string->len);
+ g_string_free (string, TRUE);
+ return found;
+}
+
+static gsize
+handle_response (MMSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
+ MMAtSerialResponseFn response_callback = (MMAtSerialResponseFn) callback;
+ GString *string;
+
+ /* Convert to a string and call the callback */
+ string = g_string_sized_new (response->len + 1);
+ g_string_append_len (string, (const char *) response->data, response->len);
+ response_callback (self, string, error, callback_data);
+ g_string_free (string, TRUE);
+
+ return response->len;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ GRegex *regex;
+ MMAtSerialUnsolicitedMsgFn callback;
+ gpointer user_data;
+ GDestroyNotify notify;
+} MMAtUnsolicitedMsgHandler;
+
+void
+mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
+ GRegex *regex,
+ MMAtSerialUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMAtUnsolicitedMsgHandler *handler;
+ MMAtSerialPortPrivate *priv;
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (regex != NULL);
+
+ handler = g_slice_new (MMAtUnsolicitedMsgHandler);
+ handler->regex = g_regex_ref (regex);
+ handler->callback = callback;
+ handler->user_data = user_data;
+ handler->notify = notify;
+
+ priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+ priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
+}
+
+static gboolean
+remove_eval_cb (const GMatchInfo *match_info,
+ GString *result,
+ gpointer user_data)
+{
+ int *result_len = (int *) user_data;
+ int start;
+ int end;
+
+ if (g_match_info_fetch_pos (match_info, 0, &start, &end))
+ *result_len -= (end - start);
+
+ return FALSE;
+}
+
+static void
+parse_unsolicited (MMSerialPort *port, GByteArray *response)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
+ MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data;
+ GMatchInfo *match_info;
+ gboolean matches;
+
+ matches = g_regex_match_full (handler->regex,
+ (const char *) response->data,
+ response->len,
+ 0, 0, &match_info, NULL);
+ if (handler->callback) {
+ while (g_match_info_matches (match_info)) {
+ handler->callback (self, match_info, handler->user_data);
+ g_match_info_next (match_info, NULL);
+ }
+ }
+
+ g_match_info_free (match_info);
+
+ if (matches) {
+ /* Remove matches */
+ char *str;
+ int result_len = response->len;
+
+ str = g_regex_replace_eval (handler->regex,
+ (const char *) response->data,
+ response->len,
+ 0, 0,
+ remove_eval_cb, &result_len, NULL);
+
+ g_byte_array_remove_range (response, 0, response->len);
+ g_byte_array_append (response, (const guint8 *) str, result_len);
+ g_free (str);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+static GByteArray *
+at_command_to_byte_array (const char *command)
+{
+ GByteArray *buf;
+ int cmdlen;
+
+ g_return_val_if_fail (command != NULL, NULL);
+
+ cmdlen = strlen (command);
+ buf = g_byte_array_sized_new (cmdlen + 3);
+
+ /* Make sure there's an AT in the front */
+ if (!g_str_has_prefix (command, "AT"))
+ g_byte_array_append (buf, (const guint8 *) "AT", 2);
+ g_byte_array_append (buf, (const guint8 *) command, cmdlen);
+
+ /* Make sure there's a trailing carriage return */
+ if (command[cmdlen] != '\r')
+ g_byte_array_append (buf, (const guint8 *) "\r", 1);
+
+ return buf;
+}
+
+void
+mm_at_serial_port_queue_command (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ GByteArray *buf;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ buf = at_command_to_byte_array (command);
+ g_return_if_fail (buf != NULL);
+
+ mm_serial_port_queue_command (MM_SERIAL_PORT (self),
+ buf,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+void
+mm_at_serial_port_queue_command_cached (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ GByteArray *buf;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ buf = at_command_to_byte_array (command);
+ g_return_if_fail (buf != NULL);
+
+ mm_serial_port_queue_command_cached (MM_SERIAL_PORT (self),
+ buf,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+static void
+debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len)
+{
+ static GString *debug = NULL;
+ const char *s;
+ GTimeVal tv;
+
+ if (!debug)
+ debug = g_string_sized_new (256);
+
+ g_string_append (debug, prefix);
+ g_string_append (debug, " '");
+
+ s = buf;
+ while (len--) {
+ if (g_ascii_isprint (*s))
+ g_string_append_c (debug, *s);
+ else if (*s == '\r')
+ g_string_append (debug, "<CR>");
+ else if (*s == '\n')
+ g_string_append (debug, "<LF>");
+ else
+ g_string_append_printf (debug, "\\%d", *s);
+
+ s++;
+ }
+
+ g_string_append_c (debug, '\'');
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): %s",
+ tv.tv_sec,
+ tv.tv_usec,
+ mm_port_get_device (MM_PORT (port)),
+ debug->str);
+ g_string_truncate (debug, 0);
+}
+
+/*****************************************************************************/
+
+MMAtSerialPort *
+mm_at_serial_port_new (const char *name, MMPortType ptype)
+{
+ return MM_AT_SERIAL_PORT (g_object_new (MM_TYPE_AT_SERIAL_PORT,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
+ MM_PORT_TYPE, ptype,
+ NULL));
+}
+
+static void
+mm_at_serial_port_init (MMAtSerialPort *self)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ MMAtSerialPort *self = MM_AT_SERIAL_PORT (object);
+ MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+
+ while (priv->unsolicited_msg_handlers) {
+ MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
+
+ if (handler->notify)
+ handler->notify (handler->user_data);
+
+ g_regex_unref (handler->regex);
+ g_slice_free (MMAtUnsolicitedMsgHandler, handler);
+ priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
+ priv->unsolicited_msg_handlers);
+ }
+
+ if (priv->response_parser_notify)
+ priv->response_parser_notify (priv->response_parser_user_data);
+
+ G_OBJECT_CLASS (mm_at_serial_port_parent_class)->finalize (object);
+}
+
+static void
+mm_at_serial_port_class_init (MMAtSerialPortClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMSerialPortClass *port_class = MM_SERIAL_PORT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMAtSerialPortPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ port_class->parse_unsolicited = parse_unsolicited;
+ port_class->parse_response = parse_response;
+ port_class->handle_response = handle_response;
+ port_class->debug_log = debug_log;
+}
diff --git a/src/mm-at-serial-port.h b/src/mm-at-serial-port.h
new file mode 100644
index 0000000..5d5f13f
--- /dev/null
+++ b/src/mm-at-serial-port.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AT_SERIAL_PORT_H
+#define MM_AT_SERIAL_PORT_H
+
+#include <glib.h>
+#include <glib/gtypes.h>
+#include <glib-object.h>
+
+#include "mm-serial-port.h"
+
+#define MM_TYPE_AT_SERIAL_PORT (mm_at_serial_port_get_type ())
+#define MM_AT_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPort))
+#define MM_AT_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortClass))
+#define MM_IS_AT_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AT_SERIAL_PORT))
+#define MM_IS_AT_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AT_SERIAL_PORT))
+#define MM_AT_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AT_SERIAL_PORT, MMAtSerialPortClass))
+
+typedef struct _MMAtSerialPort MMAtSerialPort;
+typedef struct _MMAtSerialPortClass MMAtSerialPortClass;
+
+typedef gboolean (*MMAtSerialResponseParserFn) (gpointer user_data,
+ GString *response,
+ GError **error);
+
+typedef void (*MMAtSerialUnsolicitedMsgFn) (MMAtSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data);
+
+typedef void (*MMAtSerialResponseFn) (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+struct _MMAtSerialPort {
+ MMSerialPort parent;
+};
+
+struct _MMAtSerialPortClass {
+ MMSerialPortClass parent;
+};
+
+GType mm_at_serial_port_get_type (void);
+
+MMAtSerialPort *mm_at_serial_port_new (const char *name, MMPortType ptype);
+
+void mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
+ GRegex *regex,
+ MMAtSerialUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void mm_at_serial_port_set_response_parser (MMAtSerialPort *self,
+ MMAtSerialResponseParserFn fn,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void mm_at_serial_port_queue_command (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data);
+
+void mm_at_serial_port_queue_command_cached (MMAtSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMAtSerialResponseFn callback,
+ gpointer user_data);
+
+#endif /* MM_AT_SERIAL_PORT_H */
+
diff --git a/src/mm-auth-provider-factory.c b/src/mm-auth-provider-factory.c
new file mode 100644
index 0000000..356dcf2
--- /dev/null
+++ b/src/mm-auth-provider-factory.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "mm-auth-provider.h"
+
+GObject *mm_auth_provider_new (void);
+
+#ifdef WITH_POLKIT
+#define IN_AUTH_PROVIDER_FACTORY_C
+#include "mm-auth-provider-polkit.h"
+#undef IN_AUTH_PROVIDER_FACTORY_C
+#endif
+
+MMAuthProvider *
+mm_auth_provider_get (void)
+{
+ static MMAuthProvider *singleton;
+
+ if (!singleton) {
+#if WITH_POLKIT
+ singleton = (MMAuthProvider *) mm_auth_provider_polkit_new ();
+#else
+ singleton = (MMAuthProvider *) mm_auth_provider_new ();
+#endif
+ }
+
+ g_assert (singleton);
+ return singleton;
+}
+
diff --git a/src/mm-auth-provider-polkit.c b/src/mm-auth-provider-polkit.c
new file mode 100644
index 0000000..c457eaf
--- /dev/null
+++ b/src/mm-auth-provider-polkit.c
@@ -0,0 +1,153 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <polkit/polkit.h>
+
+#include "mm-auth-request-polkit.h"
+#include "mm-auth-provider-polkit.h"
+
+G_DEFINE_TYPE (MMAuthProviderPolkit, mm_auth_provider_polkit, MM_TYPE_AUTH_PROVIDER)
+
+#define MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitPrivate))
+
+typedef struct {
+ PolkitAuthority *authority;
+ guint auth_changed_id;
+} MMAuthProviderPolkitPrivate;
+
+enum {
+ PROP_NAME = 1000,
+};
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_provider_polkit_new (void)
+{
+ return g_object_new (MM_TYPE_AUTH_PROVIDER_POLKIT, NULL);
+}
+
+/*****************************************************************************/
+
+static void
+pk_authority_changed_cb (GObject *object, gpointer user_data)
+{
+ /* Let clients know they should re-check their authorization */
+}
+
+/*****************************************************************************/
+
+static MMAuthRequest *
+real_create_request (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (provider);
+
+ return (MMAuthRequest *) mm_auth_request_polkit_new (priv->authority,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+}
+
+/*****************************************************************************/
+
+static void
+mm_auth_provider_polkit_init (MMAuthProviderPolkit *self)
+{
+ MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (self);
+
+ priv->authority = polkit_authority_get ();
+ if (priv->authority) {
+ priv->auth_changed_id = g_signal_connect (priv->authority,
+ "changed",
+ G_CALLBACK (pk_authority_changed_cb),
+ self);
+ } else
+ g_warning ("%s: failed to create PolicyKit authority.", __func__);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, "polkit");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthProviderPolkit *self = MM_AUTH_PROVIDER_POLKIT (object);
+ MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (self);
+
+ if (priv->auth_changed_id) {
+ g_signal_handler_disconnect (priv->authority, priv->auth_changed_id);
+ priv->auth_changed_id = 0;
+ }
+
+ G_OBJECT_CLASS (mm_auth_provider_polkit_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_provider_polkit_class_init (MMAuthProviderPolkitClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ MMAuthProviderClass *ap_class = MM_AUTH_PROVIDER_CLASS (class);
+
+ mm_auth_provider_polkit_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthProviderPolkitPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ ap_class->create_request = real_create_request;
+
+ /* Properties */
+ g_object_class_override_property (object_class, PROP_NAME, MM_AUTH_PROVIDER_NAME);
+}
+
diff --git a/src/mm-auth-provider-polkit.h b/src/mm-auth-provider-polkit.h
new file mode 100644
index 0000000..a52c56d
--- /dev/null
+++ b/src/mm-auth-provider-polkit.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_PROVIDER_POLKIT_H
+#define MM_AUTH_PROVIDER_POLKIT_H
+
+#include <glib-object.h>
+
+#include "mm-auth-provider.h"
+
+#define MM_TYPE_AUTH_PROVIDER_POLKIT (mm_auth_provider_polkit_get_type ())
+#define MM_AUTH_PROVIDER_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkit))
+#define MM_AUTH_PROVIDER_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitClass))
+#define MM_IS_AUTH_PROVIDER_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT))
+#define MM_IS_AUTH_PROVIDER_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_PROVIDER_POLKIT))
+#define MM_AUTH_PROVIDER_POLKIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitClass))
+
+typedef struct {
+ MMAuthProvider parent;
+} MMAuthProviderPolkit;
+
+typedef struct {
+ MMAuthProviderClass parent;
+} MMAuthProviderPolkitClass;
+
+GType mm_auth_provider_polkit_get_type (void);
+
+GObject *mm_auth_provider_polkit_new (void);
+
+#endif /* MM_AUTH_PROVIDER_POLKIT_H */
+
diff --git a/src/mm-auth-provider.c b/src/mm-auth-provider.c
new file mode 100644
index 0000000..ceff9ad
--- /dev/null
+++ b/src/mm-auth-provider.c
@@ -0,0 +1,300 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "mm-marshal.h"
+#include "mm-auth-provider.h"
+
+GObject *mm_auth_provider_new (void);
+
+G_DEFINE_TYPE (MMAuthProvider, mm_auth_provider, G_TYPE_OBJECT)
+
+#define MM_AUTH_PROVIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_PROVIDER, MMAuthProviderPrivate))
+
+typedef struct {
+ GHashTable *requests;
+ guint process_id;
+} MMAuthProviderPrivate;
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ LAST_PROP
+};
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_provider_new (void)
+{
+ return g_object_new (MM_TYPE_AUTH_PROVIDER, NULL);
+}
+
+/*****************************************************************************/
+
+static void
+remove_requests (MMAuthProvider *self, GSList *remove)
+{
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+ MMAuthRequest *req;
+
+ while (remove) {
+ req = MM_AUTH_REQUEST (remove->data);
+ g_hash_table_remove (priv->requests, req);
+ remove = g_slist_remove (remove, req);
+ }
+}
+
+void
+mm_auth_provider_cancel_request (MMAuthProvider *provider, MMAuthRequest *req)
+{
+ MMAuthProviderPrivate *priv;
+
+ g_return_if_fail (provider != NULL);
+ g_return_if_fail (MM_IS_AUTH_PROVIDER (provider));
+ g_return_if_fail (req != NULL);
+
+ priv = MM_AUTH_PROVIDER_GET_PRIVATE (provider);
+
+ g_return_if_fail (g_hash_table_lookup (priv->requests, req) != NULL);
+ g_hash_table_remove (priv->requests, req);
+}
+
+void
+mm_auth_provider_cancel_for_owner (MMAuthProvider *self, GObject *owner)
+{
+ MMAuthProviderPrivate *priv;
+ GHashTableIter iter;
+ MMAuthRequest *req;
+ gpointer value;
+ GSList *remove = NULL;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_PROVIDER (self));
+
+ /* Find all requests from this owner */
+ priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+ g_hash_table_iter_init (&iter, priv->requests);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ req = MM_AUTH_REQUEST (value);
+ if (mm_auth_request_get_owner (req) == owner)
+ remove = g_slist_prepend (remove, req);
+ }
+
+ /* And cancel/remove them */
+ remove_requests (self, remove);
+}
+
+/*****************************************************************************/
+
+
+static MMAuthRequest *
+real_create_request (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ return (MMAuthRequest *) mm_auth_request_new (0,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+}
+
+static gboolean
+process_complete_requests (gpointer user_data)
+{
+ MMAuthProvider *self = MM_AUTH_PROVIDER (user_data);
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer value;
+ GSList *remove = NULL;
+ MMAuthRequest *req;
+
+ priv->process_id = 0;
+
+ /* Call finished request's callbacks */
+ g_hash_table_iter_init (&iter, priv->requests);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ req = MM_AUTH_REQUEST (value);
+
+ if (mm_auth_request_get_authorization (req) != MM_AUTH_RESULT_UNKNOWN) {
+ mm_auth_request_callback (req);
+ remove = g_slist_prepend (remove, req);
+ }
+ }
+
+ /* And remove those requests from our pending request list */
+ remove_requests (self, remove);
+
+ return FALSE;
+}
+
+static void
+auth_result_cb (MMAuthRequest *req, gpointer user_data)
+{
+ MMAuthProvider *self = MM_AUTH_PROVIDER (user_data);
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+
+ /* Process results from an idle handler */
+ if (priv->process_id == 0)
+ priv->process_id = g_idle_add (process_complete_requests, self);
+}
+
+#define RESULT_SIGID_TAG "result-sigid"
+
+MMAuthRequest *
+mm_auth_provider_request_auth (MMAuthProvider *self,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error)
+{
+ MMAuthProviderPrivate *priv;
+ MMAuthRequest *req;
+ guint32 sigid;
+
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_AUTH_PROVIDER (self), 0);
+ g_return_val_if_fail (authorization != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+
+ req = MM_AUTH_PROVIDER_GET_CLASS (self)->create_request (self,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+ g_assert (req);
+
+ sigid = g_signal_connect (req, "result", G_CALLBACK (auth_result_cb), self);
+ g_object_set_data (G_OBJECT (req), RESULT_SIGID_TAG, GUINT_TO_POINTER (sigid));
+
+ g_hash_table_insert (priv->requests, req, req);
+ if (!mm_auth_request_authenticate (req, error)) {
+ /* Error */
+ g_hash_table_remove (priv->requests, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*****************************************************************************/
+
+static void
+dispose_auth_request (gpointer data)
+{
+ MMAuthRequest *req = MM_AUTH_REQUEST (data);
+ guint sigid;
+
+ sigid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), RESULT_SIGID_TAG));
+ if (sigid)
+ g_signal_handler_disconnect (req, sigid);
+ mm_auth_request_dispose (req);
+ g_object_unref (req);
+}
+
+static void
+mm_auth_provider_init (MMAuthProvider *self)
+{
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (self);
+
+ priv->requests = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ dispose_auth_request);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#define NULL_PROVIDER "open"
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, NULL_PROVIDER);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthProviderPrivate *priv = MM_AUTH_PROVIDER_GET_PRIVATE (object);
+
+ if (priv->process_id)
+ g_source_remove (priv->process_id);
+ g_hash_table_destroy (priv->requests);
+
+ G_OBJECT_CLASS (mm_auth_provider_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_provider_class_init (MMAuthProviderClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ mm_auth_provider_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthProviderPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ class->create_request = real_create_request;
+
+ /* Properties */
+ g_object_class_install_property (object_class, PROP_NAME,
+ g_param_spec_string (MM_AUTH_PROVIDER_NAME,
+ "Name",
+ "Provider name",
+ NULL_PROVIDER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
diff --git a/src/mm-auth-provider.h b/src/mm-auth-provider.h
new file mode 100644
index 0000000..26ff340
--- /dev/null
+++ b/src/mm-auth-provider.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_PROVIDER_H
+#define MM_AUTH_PROVIDER_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "mm-auth-request.h"
+
+/* Authorizations */
+#define MM_AUTHORIZATION_DEVICE_INFO "org.freedesktop.ModemManager.Device.Info"
+#define MM_AUTHORIZATION_DEVICE_CONTROL "org.freedesktop.ModemManager.Device.Control"
+#define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager.Contacts"
+#define MM_AUTHORIZATION_SMS "org.freedesktop.ModemManager.SMS"
+#define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager.Location"
+/******************/
+
+
+#define MM_TYPE_AUTH_PROVIDER (mm_auth_provider_get_type ())
+#define MM_AUTH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_PROVIDER, MMAuthProvider))
+#define MM_AUTH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_PROVIDER, MMAuthProviderClass))
+#define MM_IS_AUTH_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_PROVIDER))
+#define MM_IS_AUTH_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_PROVIDER))
+#define MM_AUTH_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_PROVIDER, MMAuthProviderClass))
+
+#define MM_AUTH_PROVIDER_NAME "name"
+
+typedef struct {
+ GObject parent;
+} MMAuthProvider;
+
+typedef struct {
+ GObjectClass parent;
+
+ MMAuthRequest * (*create_request) (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify);
+} MMAuthProviderClass;
+
+GType mm_auth_provider_get_type (void);
+
+/* Don't do anything clever from the notify callback... */
+MMAuthRequest *mm_auth_provider_request_auth (MMAuthProvider *provider,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error);
+
+void mm_auth_provider_cancel_for_owner (MMAuthProvider *provider,
+ GObject *owner);
+
+/* Subclass API */
+
+/* To get an auth provider instance, implemented in mm-auth-provider-factory.c */
+MMAuthProvider *mm_auth_provider_get (void);
+
+/* schedules the request's completion */
+void mm_auth_provider_finish_request (MMAuthProvider *provider,
+ MMAuthRequest *req,
+ MMAuthResult result);
+
+void mm_auth_provider_cancel_request (MMAuthProvider *provider, MMAuthRequest *req);
+
+#endif /* MM_AUTH_PROVIDER_H */
+
diff --git a/src/mm-auth-request-polkit.c b/src/mm-auth-request-polkit.c
new file mode 100644
index 0000000..2a96bfe
--- /dev/null
+++ b/src/mm-auth-request-polkit.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "mm-auth-request-polkit.h"
+
+G_DEFINE_TYPE (MMAuthRequestPolkit, mm_auth_request_polkit, MM_TYPE_AUTH_REQUEST)
+
+#define MM_AUTH_REQUEST_POLKIT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkitPrivate))
+
+typedef struct {
+ PolkitAuthority *authority;
+ GCancellable *cancellable;
+ PolkitSubject *subject;
+} MMAuthRequestPolkitPrivate;
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_request_polkit_new (PolkitAuthority *authority,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ GObject *obj;
+ MMAuthRequestPolkitPrivate *priv;
+ char *sender;
+
+ g_return_val_if_fail (authorization != NULL, NULL);
+ g_return_val_if_fail (owner != NULL, NULL);
+ g_return_val_if_fail (callback != NULL, NULL);
+ g_return_val_if_fail (context != NULL, NULL);
+
+ obj = mm_auth_request_new (MM_TYPE_AUTH_REQUEST_POLKIT,
+ authorization,
+ owner,
+ context,
+ callback,
+ callback_data,
+ notify);
+ if (obj) {
+ priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (obj);
+ priv->authority = authority;
+ priv->cancellable = g_cancellable_new ();
+
+ sender = dbus_g_method_get_sender (context);
+ priv->subject = polkit_system_bus_name_new (sender);
+ g_free (sender);
+ }
+
+ return obj;
+}
+
+/*****************************************************************************/
+
+static void
+pk_auth_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ MMAuthRequestPolkit *self = user_data;
+ MMAuthRequestPolkitPrivate *priv;
+ PolkitAuthorizationResult *pk_result;
+ GError *error = NULL;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST_POLKIT (self));
+
+ priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (self);
+ if (!g_cancellable_is_cancelled (priv->cancellable)) {
+ pk_result = polkit_authority_check_authorization_finish (priv->authority,
+ result,
+ &error);
+ if (error) {
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_INTERNAL_FAILURE);
+ g_warning ("%s: PolicyKit authentication error: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ } else if (polkit_authorization_result_get_is_authorized (pk_result))
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_AUTHORIZED);
+ else if (polkit_authorization_result_get_is_challenge (pk_result))
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_CHALLENGE);
+ else
+ mm_auth_request_set_result (MM_AUTH_REQUEST (self), MM_AUTH_RESULT_NOT_AUTHORIZED);
+
+ g_signal_emit_by_name (self, "result");
+ }
+
+ g_object_unref (self);
+}
+
+static gboolean
+real_authenticate (MMAuthRequest *self, GError **error)
+{
+ MMAuthRequestPolkitPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST_POLKIT (self), FALSE);
+
+ /* We ref ourselves across the polkit call, because we can't get
+ * disposed of while the call is still in-progress, and even if we
+ * cancel ourselves we'll still get the callback.
+ */
+ g_object_ref (self);
+
+ priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (self);
+ polkit_authority_check_authorization (priv->authority,
+ priv->subject,
+ mm_auth_request_get_authorization (MM_AUTH_REQUEST (self)),
+ NULL,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+ priv->cancellable,
+ pk_auth_cb,
+ self);
+ return TRUE;
+}
+
+static void
+real_dispose (MMAuthRequest *req)
+{
+ g_return_if_fail (req != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST_POLKIT (req));
+
+ g_cancellable_cancel (MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (req)->cancellable);
+}
+
+/*****************************************************************************/
+
+static void
+mm_auth_request_polkit_init (MMAuthRequestPolkit *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthRequestPolkitPrivate *priv = MM_AUTH_REQUEST_POLKIT_GET_PRIVATE (object);
+
+ g_object_unref (priv->cancellable);
+ g_object_unref (priv->subject);
+
+ G_OBJECT_CLASS (mm_auth_request_polkit_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_request_polkit_class_init (MMAuthRequestPolkitClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ MMAuthRequestClass *ar_class = MM_AUTH_REQUEST_CLASS (class);
+
+ mm_auth_request_polkit_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthRequestPolkitPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ ar_class->authenticate = real_authenticate;
+ ar_class->dispose = real_dispose;
+}
+
diff --git a/src/mm-auth-request-polkit.h b/src/mm-auth-request-polkit.h
new file mode 100644
index 0000000..384ace8
--- /dev/null
+++ b/src/mm-auth-request-polkit.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_REQUEST_POLKIT_H
+#define MM_AUTH_REQUEST_POLKIT_H
+
+#include <glib-object.h>
+#include <polkit/polkit.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "mm-auth-request.h"
+
+#define MM_TYPE_AUTH_REQUEST_POLKIT (mm_auth_request_polkit_get_type ())
+#define MM_AUTH_REQUEST_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkit))
+#define MM_AUTH_REQUEST_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkitClass))
+#define MM_IS_AUTH_REQUEST_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_REQUEST_POLKIT))
+#define MM_IS_AUTH_REQUEST_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_REQUEST_POLKIT))
+#define MM_AUTH_REQUEST_POLKIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_REQUEST_POLKIT, MMAuthRequestPolkitClass))
+
+typedef struct {
+ MMAuthRequest parent;
+} MMAuthRequestPolkit;
+
+typedef struct {
+ MMAuthRequestClass parent;
+} MMAuthRequestPolkitClass;
+
+GType mm_auth_request_polkit_get_type (void);
+
+GObject *mm_auth_request_polkit_new (PolkitAuthority *authority,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify);
+
+void mm_auth_request_polkit_cancel (MMAuthRequestPolkit *self);
+
+#endif /* MM_AUTH_REQUEST_POLKIT_H */
+
diff --git a/src/mm-auth-request.c b/src/mm-auth-request.c
new file mode 100644
index 0000000..79f0d42
--- /dev/null
+++ b/src/mm-auth-request.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include "mm-auth-request.h"
+
+G_DEFINE_TYPE (MMAuthRequest, mm_auth_request, G_TYPE_OBJECT)
+
+#define MM_AUTH_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_AUTH_REQUEST, MMAuthRequestPrivate))
+
+typedef struct {
+ GObject *owner;
+ char *auth;
+ DBusGMethodInvocation *context;
+ MMAuthRequestCb callback;
+ gpointer callback_data;
+
+ MMAuthResult result;
+} MMAuthRequestPrivate;
+
+/*****************************************************************************/
+
+GObject *
+mm_auth_request_new (GType atype,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify)
+{
+ GObject *obj;
+ MMAuthRequestPrivate *priv;
+
+ g_return_val_if_fail (authorization != NULL, NULL);
+ g_return_val_if_fail (owner != NULL, NULL);
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ obj = g_object_new (atype ? atype : MM_TYPE_AUTH_REQUEST, NULL);
+ if (obj) {
+ priv = MM_AUTH_REQUEST_GET_PRIVATE (obj);
+ priv->owner = owner; /* not reffed */
+ priv->context = context;
+ priv->auth = g_strdup (authorization);
+ priv->callback = callback;
+ priv->callback_data = callback_data;
+
+ g_object_set_data_full (obj, "caller-data", callback_data, notify);
+ }
+
+ return obj;
+}
+
+/*****************************************************************************/
+
+const char *
+mm_auth_request_get_authorization (MMAuthRequest *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST (self), NULL);
+
+ return MM_AUTH_REQUEST_GET_PRIVATE (self)->auth;
+}
+
+GObject *
+mm_auth_request_get_owner (MMAuthRequest *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST (self), NULL);
+
+ return MM_AUTH_REQUEST_GET_PRIVATE (self)->owner;
+}
+
+MMAuthResult
+mm_auth_request_get_result (MMAuthRequest *self)
+{
+ g_return_val_if_fail (self != NULL, MM_AUTH_RESULT_UNKNOWN);
+ g_return_val_if_fail (MM_IS_AUTH_REQUEST (self), MM_AUTH_RESULT_UNKNOWN);
+
+ return MM_AUTH_REQUEST_GET_PRIVATE (self)->result;
+}
+
+void
+mm_auth_request_set_result (MMAuthRequest *self, MMAuthResult result)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST (self));
+ g_return_if_fail (result != MM_AUTH_RESULT_UNKNOWN);
+
+ MM_AUTH_REQUEST_GET_PRIVATE (self)->result = result;
+}
+
+gboolean
+mm_auth_request_authenticate (MMAuthRequest *self, GError **error)
+{
+ return MM_AUTH_REQUEST_GET_CLASS (self)->authenticate (self, error);
+}
+
+void
+mm_auth_request_callback (MMAuthRequest *self)
+{
+ MMAuthRequestPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST (self));
+
+ priv = MM_AUTH_REQUEST_GET_PRIVATE (self);
+ g_warn_if_fail (priv->result != MM_AUTH_RESULT_UNKNOWN);
+
+ if (priv->callback)
+ priv->callback (self, priv->owner, priv->context, priv->callback_data);
+}
+
+void
+mm_auth_request_dispose (MMAuthRequest *self)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_AUTH_REQUEST (self));
+
+ if (MM_AUTH_REQUEST_GET_CLASS (self)->dispose)
+ MM_AUTH_REQUEST_GET_CLASS (self)->dispose (self);
+}
+
+/*****************************************************************************/
+
+static gboolean
+real_authenticate (MMAuthRequest *self, GError **error)
+{
+ /* Null auth; everything passes */
+ mm_auth_request_set_result (self, MM_AUTH_RESULT_AUTHORIZED);
+ g_signal_emit_by_name (self, "result");
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+mm_auth_request_init (MMAuthRequest *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ MMAuthRequestPrivate *priv = MM_AUTH_REQUEST_GET_PRIVATE (object);
+
+ g_free (priv->auth);
+
+ G_OBJECT_CLASS (mm_auth_request_parent_class)->dispose (object);
+}
+
+static void
+mm_auth_request_class_init (MMAuthRequestClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ mm_auth_request_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (MMAuthRequestPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ class->authenticate = real_authenticate;
+
+ g_signal_new ("result",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+}
+
diff --git a/src/mm-auth-request.h b/src/mm-auth-request.h
new file mode 100644
index 0000000..e22f0a2
--- /dev/null
+++ b/src/mm-auth-request.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_AUTH_REQUEST_H
+#define MM_AUTH_REQUEST_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define MM_TYPE_AUTH_REQUEST (mm_auth_request_get_type ())
+#define MM_AUTH_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_REQUEST, MMAuthRequest))
+#define MM_AUTH_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_REQUEST, MMAuthRequestClass))
+#define MM_IS_AUTH_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_REQUEST))
+#define MM_IS_AUTH_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_REQUEST))
+#define MM_AUTH_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_REQUEST, MMAuthRequestClass))
+
+typedef enum MMAuthResult {
+ MM_AUTH_RESULT_UNKNOWN = 0,
+ MM_AUTH_RESULT_INTERNAL_FAILURE,
+ MM_AUTH_RESULT_NOT_AUTHORIZED,
+ MM_AUTH_RESULT_CHALLENGE,
+ MM_AUTH_RESULT_AUTHORIZED
+} MMAuthResult;
+
+typedef struct {
+ GObject parent;
+} MMAuthRequest;
+
+typedef struct {
+ GObjectClass parent;
+
+ gboolean (*authenticate) (MMAuthRequest *self, GError **error);
+ void (*dispose) (MMAuthRequest *self);
+} MMAuthRequestClass;
+
+GType mm_auth_request_get_type (void);
+
+typedef void (*MMAuthRequestCb) (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data);
+
+GObject *mm_auth_request_new (GType atype,
+ const char *authorization,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify);
+
+const char * mm_auth_request_get_authorization (MMAuthRequest *req);
+GObject * mm_auth_request_get_owner (MMAuthRequest *req);
+MMAuthResult mm_auth_request_get_result (MMAuthRequest *req);
+void mm_auth_request_set_result (MMAuthRequest *req, MMAuthResult result);
+gboolean mm_auth_request_authenticate (MMAuthRequest *req, GError **error);
+void mm_auth_request_callback (MMAuthRequest *req);
+void mm_auth_request_dispose (MMAuthRequest *req);
+
+#endif /* MM_AUTH_REQUEST_H */
+
diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c
index 1882898..1986bb5 100644
--- a/src/mm-callback-info.c
+++ b/src/mm-callback-info.c
@@ -91,6 +91,8 @@ mm_callback_info_schedule (MMCallbackInfo *info)
g_return_if_fail (info->pending_id == 0);
g_return_if_fail (info->called == FALSE);
+ g_warn_if_fail (info->chain_left == 0);
+
info->pending_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, callback_info_do, info, callback_info_done);
}
@@ -208,3 +210,24 @@ mm_callback_info_unref (MMCallbackInfo *info)
}
}
+void
+mm_callback_info_chain_start (MMCallbackInfo *info, guint num)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (num > 0);
+ g_return_if_fail (info->chain_left == 0);
+
+ info->chain_left = num;
+}
+
+void
+mm_callback_info_chain_complete_one (MMCallbackInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->chain_left > 0);
+
+ info->chain_left--;
+ if (info->chain_left == 0)
+ mm_callback_info_schedule (info);
+}
+
diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h
index 66c2048..ef4d65f 100644
--- a/src/mm-callback-info.h
+++ b/src/mm-callback-info.h
@@ -25,6 +25,9 @@ typedef void (*MMCallbackInfoInvokeFn) (MMCallbackInfo *info);
struct _MMCallbackInfo {
guint32 refcount;
+ /* # of ops left in this callback chain */
+ guint32 chain_left;
+
GData *qdata;
MMModem *modem;
@@ -70,5 +73,8 @@ gpointer mm_callback_info_get_data (MMCallbackInfo *info,
MMCallbackInfo *mm_callback_info_ref (MMCallbackInfo *info);
void mm_callback_info_unref (MMCallbackInfo *info);
+void mm_callback_info_chain_start (MMCallbackInfo *info, guint num);
+void mm_callback_info_chain_complete_one (MMCallbackInfo *info);
+
#endif /* MM_CALLBACK_INFO_H */
diff --git a/src/mm-charsets.c b/src/mm-charsets.c
new file mode 100644
index 0000000..c75c3a9
--- /dev/null
+++ b/src/mm-charsets.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-charsets.h"
+#include "mm-utils.h"
+
+typedef struct {
+ const char *gsm_name;
+ const char *other_name;
+ const char *iconv_from_name;
+ const char *iconv_to_name;
+ MMModemCharset charset;
+} CharsetEntry;
+
+static CharsetEntry charset_map[] = {
+ { "UTF-8", "UTF8", "UTF-8", "UTF-8//TRANSLIT", MM_MODEM_CHARSET_UTF8 },
+ { "UCS2", NULL, "UCS-2BE", "UCS-2BE//TRANSLIT", MM_MODEM_CHARSET_UCS2 },
+ { "IRA", "ASCII", "ASCII", "ASCII//TRANSLIT", MM_MODEM_CHARSET_IRA },
+ { "GSM", NULL, NULL, NULL, MM_MODEM_CHARSET_GSM },
+ { "8859-1", NULL, "ISO8859-1", "ISO8859-1//TRANSLIT", MM_MODEM_CHARSET_8859_1 },
+ { "PCCP437", NULL, NULL, NULL, MM_MODEM_CHARSET_PCCP437 },
+ { "PCDN", NULL, NULL, NULL, MM_MODEM_CHARSET_PCDN },
+ { "HEX", NULL, NULL, NULL, MM_MODEM_CHARSET_HEX },
+ { NULL, NULL, NULL, NULL, MM_MODEM_CHARSET_UNKNOWN }
+};
+
+const char *
+mm_modem_charset_to_string (MMModemCharset charset)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ while (iter->gsm_name) {
+ if (iter->charset == charset)
+ return iter->gsm_name;
+ iter++;
+ }
+ g_warn_if_reached ();
+ return NULL;
+}
+
+MMModemCharset
+mm_modem_charset_from_string (const char *string)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (string != NULL, MM_MODEM_CHARSET_UNKNOWN);
+
+ while (iter->gsm_name) {
+ if (strcasestr (string, iter->gsm_name))
+ return iter->charset;
+ if (iter->other_name && strcasestr (string, iter->other_name))
+ return iter->charset;
+ iter++;
+ }
+ return MM_MODEM_CHARSET_UNKNOWN;
+}
+
+static const char *
+charset_iconv_to (MMModemCharset charset)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ while (iter->gsm_name) {
+ if (iter->charset == charset)
+ return iter->iconv_to_name;
+ iter++;
+ }
+ g_warn_if_reached ();
+ return NULL;
+}
+
+static const char *
+charset_iconv_from (MMModemCharset charset)
+{
+ CharsetEntry *iter = &charset_map[0];
+
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ while (iter->gsm_name) {
+ if (iter->charset == charset)
+ return iter->iconv_from_name;
+ iter++;
+ }
+ g_warn_if_reached ();
+ return NULL;
+}
+
+gboolean
+mm_modem_charset_byte_array_append (GByteArray *array,
+ const char *string,
+ gboolean quoted,
+ MMModemCharset charset)
+{
+ const char *iconv_to;
+ char *converted;
+ GError *error = NULL;
+ gsize written = 0;
+
+ g_return_val_if_fail (array != NULL, FALSE);
+ g_return_val_if_fail (string != NULL, FALSE);
+
+ iconv_to = charset_iconv_to (charset);
+ g_return_val_if_fail (iconv_to != NULL, FALSE);
+
+ converted = g_convert (string,
+ g_utf8_strlen (string, -1),
+ iconv_to,
+ "UTF-8",
+ NULL,
+ &written,
+ &error);
+ if (!converted) {
+ if (error) {
+ g_warning ("%s: failed to convert '%s' to %s character set: (%d) %s",
+ __func__, string, iconv_to,
+ error->code, error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+ }
+
+ if (quoted)
+ g_byte_array_append (array, (const guint8 *) "\"", 1);
+ g_byte_array_append (array, (const guint8 *) converted, written);
+ if (quoted)
+ g_byte_array_append (array, (const guint8 *) "\"", 1);
+
+ g_free (converted);
+ return TRUE;
+}
+
+char *
+mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset)
+{
+ char *unconverted;
+ const char *iconv_from;
+ gsize unconverted_len = 0;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+
+ iconv_from = charset_iconv_from (charset);
+ g_return_val_if_fail (iconv_from != NULL, FALSE);
+
+ unconverted = utils_hexstr2bin (src, &unconverted_len);
+ g_return_val_if_fail (unconverted != NULL, NULL);
+
+ if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA)
+ return unconverted;
+
+ return g_convert (unconverted, unconverted_len, "UTF-8//TRANSLIT", iconv_from, NULL, NULL, NULL);
+}
+
diff --git a/src/mm-charsets.h b/src/mm-charsets.h
new file mode 100644
index 0000000..5fa3406
--- /dev/null
+++ b/src/mm-charsets.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_CHARSETS_H
+#define MM_CHARSETS_H
+
+#include <glib.h>
+
+typedef enum {
+ MM_MODEM_CHARSET_UNKNOWN = 0x00000000,
+ MM_MODEM_CHARSET_GSM = 0x00000001,
+ MM_MODEM_CHARSET_IRA = 0x00000002,
+ MM_MODEM_CHARSET_8859_1 = 0x00000004,
+ MM_MODEM_CHARSET_UTF8 = 0x00000008,
+ MM_MODEM_CHARSET_UCS2 = 0x00000010,
+ MM_MODEM_CHARSET_PCCP437 = 0x00000020,
+ MM_MODEM_CHARSET_PCDN = 0x00000040,
+ MM_MODEM_CHARSET_HEX = 0x00000080
+} MMModemCharset;
+
+const char *mm_modem_charset_to_string (MMModemCharset charset);
+
+MMModemCharset mm_modem_charset_from_string (const char *string);
+
+/* Append the given string to the given byte array but re-encode it
+ * into the given charset first. The original string is assumed to be
+ * UTF-8 encoded.
+ */
+gboolean mm_modem_charset_byte_array_append (GByteArray *array,
+ const char *string,
+ gboolean quoted,
+ MMModemCharset charset);
+
+/* Take a string in hex representation ("00430052" or "A4BE11" for example)
+ * and convert it from the given character set to UTF-8.
+ */
+char *mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset);
+
+#endif /* MM_CHARSETS_H */
+
diff --git a/src/mm-errors.c b/src/mm-errors.c
index 34f56c1..e4fdda7 100644
--- a/src/mm-errors.c
+++ b/src/mm-errors.c
@@ -16,6 +16,9 @@
#include "mm-errors.h"
+#include <string.h>
+#include <ctype.h>
+
#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
GQuark
@@ -36,10 +39,12 @@ mm_serial_error_get_type (void)
if (etype == 0) {
static const GEnumValue values[] = {
- ENUM_ENTRY (MM_SERIAL_OPEN_FAILED, "SerialOpenFailed"),
- ENUM_ENTRY (MM_SERIAL_SEND_FAILED, "SerialSendfailed"),
- ENUM_ENTRY (MM_SERIAL_RESPONSE_TIMEOUT, "SerialResponseTimeout"),
- ENUM_ENTRY (MM_SERIAL_OPEN_FAILED_NO_DEVICE, "SerialOpenFailedNoDevice"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_OPEN_FAILED, "SerialOpenFailed"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_SEND_FAILED, "SerialSendfailed"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_RESPONSE_TIMEOUT, "SerialResponseTimeout"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE, "SerialOpenFailedNoDevice"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_FLASH_FAILED, "SerialFlashFailed"),
+ ENUM_ENTRY (MM_SERIAL_ERROR_NOT_OPEN, "SerialNotOpen"),
{ 0, 0, 0 }
};
@@ -73,6 +78,8 @@ mm_modem_error_get_type (void)
ENUM_ENTRY (MM_MODEM_ERROR_DISCONNECTED, "Disconnected"),
ENUM_ENTRY (MM_MODEM_ERROR_OPERATION_IN_PROGRESS, "OperationInProgress"),
ENUM_ENTRY (MM_MODEM_ERROR_REMOVED, "Removed"),
+ ENUM_ENTRY (MM_MODEM_ERROR_AUTHORIZATION_REQUIRED, "AuthorizationRequired"),
+ ENUM_ENTRY (MM_MODEM_ERROR_UNSUPPORTED_CHARSET, "UnsupportedCharset"),
{ 0, 0, 0 }
};
@@ -222,69 +229,127 @@ mm_mobile_error_get_type (void)
return etype;
}
+typedef struct {
+ int code;
+ const char *error; /* lowercase, and stripped of special chars and whitespace */
+ const char *message;
+} ErrorTable;
+
+static ErrorTable errors[] = {
+ { MM_MOBILE_ERROR_PHONE_FAILURE, "phonefailure", "Phone failure" },
+ { MM_MOBILE_ERROR_NO_CONNECTION, "noconnectiontophone", "No connection to phone" },
+ { MM_MOBILE_ERROR_LINK_RESERVED, "phoneadapterlinkreserved", "Phone-adaptor link reserved" },
+ { MM_MOBILE_ERROR_NOT_ALLOWED, "operationnotallowed", "Operation not allowed" },
+ { MM_MOBILE_ERROR_NOT_SUPPORTED, "operationnotsupported", "Operation not supported" },
+ { MM_MOBILE_ERROR_PH_SIM_PIN, "phsimpinrequired", "PH-SIM PIN required" },
+ { MM_MOBILE_ERROR_PH_FSIM_PIN, "phfsimpinrequired", "PH-FSIM PIN required" },
+ { MM_MOBILE_ERROR_PH_FSIM_PUK, "phfsimpukrequired", "PH-FSIM PUK required" },
+ { MM_MOBILE_ERROR_SIM_NOT_INSERTED, "simnotinserted", "SIM not inserted" },
+ { MM_MOBILE_ERROR_SIM_PIN, "simpinrequired", "SIM PIN required" },
+ { MM_MOBILE_ERROR_SIM_PUK, "simpukrequired", "SIM PUK required" },
+ { MM_MOBILE_ERROR_SIM_FAILURE, "simfailure", "SIM failure" },
+ { MM_MOBILE_ERROR_SIM_BUSY, "simbusy", "SIM busy" },
+ { MM_MOBILE_ERROR_SIM_WRONG, "simwrong", "SIM wrong" },
+ { MM_MOBILE_ERROR_WRONG_PASSWORD, "incorrectpassword", "Incorrect password" },
+ { MM_MOBILE_ERROR_SIM_PIN2, "simpin2required", "SIM PIN2 required" },
+ { MM_MOBILE_ERROR_SIM_PUK2, "simpuk2required", "SIM PUK2 required" },
+ { MM_MOBILE_ERROR_MEMORY_FULL, "memoryfull", "Memory full" },
+ { MM_MOBILE_ERROR_INVALID_INDEX, "invalidindex", "Invalid index" },
+ { MM_MOBILE_ERROR_NOT_FOUND, "notfound", "Not found" },
+ { MM_MOBILE_ERROR_MEMORY_FAILURE, "memoryfailure", "Memory failure" },
+ { MM_MOBILE_ERROR_TEXT_TOO_LONG, "textstringtoolong", "Text string too long" },
+ { MM_MOBILE_ERROR_INVALID_CHARS, "invalidcharactersintextstring", "Invalid characters in text string" },
+ { MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG, "dialstringtoolong", "Dial string too long" },
+ { MM_MOBILE_ERROR_DIAL_STRING_INVALID, "invalidcharactersindialstring", "Invalid characters in dial string" },
+ { MM_MOBILE_ERROR_NO_NETWORK, "nonetworkservice", "No network service" },
+ { MM_MOBILE_ERROR_NETWORK_TIMEOUT, "networktimeout", "Network timeout" },
+ { MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED, "networknotallowedemergencycallsonly", "Network not allowed - emergency calls only" },
+ { MM_MOBILE_ERROR_NETWORK_PIN, "networkpersonalizationpinrequired", "Network personalization PIN required" },
+ { MM_MOBILE_ERROR_NETWORK_PUK, "networkpersonalizationpukrequired", "Network personalization PUK required" },
+ { MM_MOBILE_ERROR_NETWORK_SUBSET_PIN, "networksubsetpersonalizationpinrequired", "Network subset personalization PIN required" },
+ { MM_MOBILE_ERROR_NETWORK_SUBSET_PUK, "networksubsetpersonalizationpukrequired", "Network subset personalization PUK required" },
+ { MM_MOBILE_ERROR_SERVICE_PIN, "serviceproviderpersonalizationpinrequired", "Service provider personalization PIN required" },
+ { MM_MOBILE_ERROR_SERVICE_PUK, "serviceproviderpersonalizationpukrequired", "Service provider personalization PUK required" },
+ { MM_MOBILE_ERROR_CORP_PIN, "corporatepersonalizationpinrequired", "Corporate personalization PIN required" },
+ { MM_MOBILE_ERROR_CORP_PUK, "corporatepersonalizationpukrequired", "Corporate personalization PUK required" },
+ { MM_MOBILE_ERROR_HIDDEN_KEY, "phsimpukrequired", "Hidden key required" },
+ { MM_MOBILE_ERROR_EAP_NOT_SUPPORTED, "eapmethodnotsupported", "EAP method not supported" },
+ { MM_MOBILE_ERROR_INCORRECT_PARAMS, "incorrectparameters", "Incorrect parameters" },
+ { MM_MOBILE_ERROR_UNKNOWN, "unknownerror", "Unknown error" },
+ { MM_MOBILE_ERROR_GPRS_ILLEGAL_MS, "illegalms", "Illegal MS" },
+ { MM_MOBILE_ERROR_GPRS_ILLEGAL_ME, "illegalme", "Illegal ME" },
+ { MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED, "gprsservicesnotallowed", "GPRS services not allowed" },
+ { MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED, "plmnnotallowed", "PLMN not allowed" },
+ { MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED, "locationareanotallowed", "Location area not allowed" },
+ { MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED, "roamingnotallowedinthislocationarea", "Roaming not allowed in this location area" },
+ { MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED, "serviceoperationnotsupported", "Service option not supported" },
+ { MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED, "requestedserviceoptionnotsubscribed", "Requested service option not subscribed" },
+ { MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER, "serviceoptiontemporarilyoutoforder", "Service option temporarily out of order" },
+ { MM_MOBILE_ERROR_GPRS_UNKNOWN, "unspecifiedgprserror", "Unspecified GPRS error" },
+ { MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE, "pdpauthenticationfailure", "PDP authentication failure" },
+ { MM_MOBILE_ERROR_GPRS_INVALID_CLASS, "invalidmobileclass", "Invalid mobile class" },
+ { -1, NULL, NULL }
+};
+
GError *
mm_mobile_error_for_code (int error_code)
{
- const char *msg;
+ const char *msg = NULL;
+ const ErrorTable *ptr = &errors[0];
+
+ while (ptr->code >= 0) {
+ if (ptr->code == error_code) {
+ msg = ptr->message;
+ break;
+ }
+ ptr++;
+ }
- switch (error_code) {
- case MM_MOBILE_ERROR_PHONE_FAILURE: msg = "Phone failure"; break;
- case MM_MOBILE_ERROR_NO_CONNECTION: msg = "No connection to phone"; break;
- case MM_MOBILE_ERROR_LINK_RESERVED: msg = "Phone-adaptor link reserved"; break;
- case MM_MOBILE_ERROR_NOT_ALLOWED: msg = "Operation not allowed"; break;
- case MM_MOBILE_ERROR_NOT_SUPPORTED: msg = "Operation not supported"; break;
- case MM_MOBILE_ERROR_PH_SIM_PIN: msg = "PH-SIM PIN required"; break;
- case MM_MOBILE_ERROR_PH_FSIM_PIN: msg = "PH-FSIM PIN required"; break;
- case MM_MOBILE_ERROR_PH_FSIM_PUK: msg = "PH-FSIM PUK required"; break;
- case MM_MOBILE_ERROR_SIM_NOT_INSERTED: msg = "SIM not inserted"; break;
- case MM_MOBILE_ERROR_SIM_PIN: msg = "SIM PIN required"; break;
- case MM_MOBILE_ERROR_SIM_PUK: msg = "SIM PUK required"; break;
- case MM_MOBILE_ERROR_SIM_FAILURE: msg = "SIM failure"; break;
- case MM_MOBILE_ERROR_SIM_BUSY: msg = "SIM busy"; break;
- case MM_MOBILE_ERROR_SIM_WRONG: msg = "SIM wrong"; break;
- case MM_MOBILE_ERROR_WRONG_PASSWORD: msg = "Incorrect password"; break;
- case MM_MOBILE_ERROR_SIM_PIN2: msg = "SIM PIN2 required"; break;
- case MM_MOBILE_ERROR_SIM_PUK2: msg = "SIM PUK2 required"; break;
- case MM_MOBILE_ERROR_MEMORY_FULL: msg = "Memory full"; break;
- case MM_MOBILE_ERROR_INVALID_INDEX: msg = "Invalid index"; break;
- case MM_MOBILE_ERROR_NOT_FOUND: msg = "Not found"; break;
- case MM_MOBILE_ERROR_MEMORY_FAILURE: msg = "Memory failure"; break;
- case MM_MOBILE_ERROR_TEXT_TOO_LONG: msg = "Text string too long"; break;
- case MM_MOBILE_ERROR_INVALID_CHARS: msg = "Invalid characters in text string"; break;
- case MM_MOBILE_ERROR_DIAL_STRING_TOO_LONG: msg = "Dial string too long"; break;
- case MM_MOBILE_ERROR_DIAL_STRING_INVALID: msg = "Invalid characters in dial string"; break;
- case MM_MOBILE_ERROR_NO_NETWORK: msg = "No network service"; break;
- case MM_MOBILE_ERROR_NETWORK_TIMEOUT: msg = "Network timeout"; break;
- case MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED: msg = "Network not allowed - emergency calls only"; break;
- case MM_MOBILE_ERROR_NETWORK_PIN: msg = "Network personalization PIN required"; break;
- case MM_MOBILE_ERROR_NETWORK_PUK: msg = "Network personalization PUK required"; break;
- case MM_MOBILE_ERROR_NETWORK_SUBSET_PIN: msg = "Network subset personalization PIN required"; break;
- case MM_MOBILE_ERROR_NETWORK_SUBSET_PUK: msg = "Network subset personalization PUK required"; break;
- case MM_MOBILE_ERROR_SERVICE_PIN: msg = "Service provider personalization PIN required"; break;
- case MM_MOBILE_ERROR_SERVICE_PUK: msg = "Service provider personalization PUK required"; break;
- case MM_MOBILE_ERROR_CORP_PIN: msg = "Corporate personalization PIN required"; break;
- case MM_MOBILE_ERROR_CORP_PUK: msg = "Corporate personalization PUK required"; break;
- case MM_MOBILE_ERROR_HIDDEN_KEY: msg = "Hidden key required"; break;
- case MM_MOBILE_ERROR_EAP_NOT_SUPPORTED: msg = "EAP method not supported"; break;
- case MM_MOBILE_ERROR_INCORRECT_PARAMS: msg = "Incorrect parameters"; break;
- case MM_MOBILE_ERROR_UNKNOWN: msg = "Unknown error"; break;
- case MM_MOBILE_ERROR_GPRS_ILLEGAL_MS: msg = "Illegal MS"; break;
- case MM_MOBILE_ERROR_GPRS_ILLEGAL_ME: msg = "Illegal ME"; break;
- case MM_MOBILE_ERROR_GPRS_SERVICE_NOT_ALLOWED: msg = "GPRS services not allowed"; break;
- case MM_MOBILE_ERROR_GPRS_PLMN_NOT_ALLOWED: msg = "PLMN not allowed"; break;
- case MM_MOBILE_ERROR_GPRS_LOCATION_NOT_ALLOWED: msg = "Location area not allowed"; break;
- case MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED: msg = "Roaming not allowed in this location area"; break;
- case MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED: msg = "Service option not supported"; break;
- case MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED: msg = "Requested service option not subscribed"; break;
- case MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER: msg = "Service option temporarily out of order"; break;
- case MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE: msg = "PDP authentication failure"; break;
- case MM_MOBILE_ERROR_GPRS_UNKNOWN: msg = "Unspecified GPRS error"; break;
- case MM_MOBILE_ERROR_GPRS_INVALID_CLASS: msg = "Invalid mobile class"; break;
- default:
- g_warning ("Invalid error code");
+ if (!msg) {
+ g_warning ("Invalid error code: %d", error_code);
error_code = MM_MOBILE_ERROR_UNKNOWN;
msg = "Unknown error";
}
return g_error_new_literal (MM_MOBILE_ERROR, error_code, msg);
}
+
+#define BUF_SIZE 100
+
+GError *
+mm_mobile_error_for_string (const char *str)
+{
+ int error_code = -1;
+ const ErrorTable *ptr = &errors[0];
+ char buf[BUF_SIZE + 1];
+ const char *msg = NULL, *p = str;
+ int i = 0;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ /* Normalize the error code by stripping whitespace and odd characters */
+ while (*p && i < BUF_SIZE) {
+ if (isalnum (*p))
+ buf[i++] = tolower (*p);
+ p++;
+ }
+ buf[i] = '\0';
+
+ while (ptr->code >= 0) {
+ if (!strcmp (buf, ptr->error)) {
+ error_code = ptr->code;
+ msg = ptr->message;
+ break;
+ }
+ ptr++;
+ }
+
+ if (!msg) {
+ g_warning ("Invalid error code: %d", error_code);
+ error_code = MM_MOBILE_ERROR_UNKNOWN;
+ msg = "Unknown error";
+ }
+
+ return g_error_new_literal (MM_MOBILE_ERROR, error_code, msg);
+}
+
diff --git a/src/mm-errors.h b/src/mm-errors.h
index c02a351..13a531d 100644
--- a/src/mm-errors.h
+++ b/src/mm-errors.h
@@ -20,10 +20,12 @@
#include <glib-object.h>
enum {
- MM_SERIAL_OPEN_FAILED = 0,
- MM_SERIAL_SEND_FAILED = 1,
- MM_SERIAL_RESPONSE_TIMEOUT = 2,
- MM_SERIAL_OPEN_FAILED_NO_DEVICE = 3
+ MM_SERIAL_ERROR_OPEN_FAILED = 0,
+ MM_SERIAL_ERROR_SEND_FAILED = 1,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT = 2,
+ MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE = 3,
+ MM_SERIAL_ERROR_FLASH_FAILED = 4,
+ MM_SERIAL_ERROR_NOT_OPEN = 5,
};
#define MM_SERIAL_ERROR (mm_serial_error_quark ())
@@ -39,7 +41,9 @@ enum {
MM_MODEM_ERROR_CONNECTED = 2,
MM_MODEM_ERROR_DISCONNECTED = 3,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS = 4,
- MM_MODEM_ERROR_REMOVED = 5
+ MM_MODEM_ERROR_REMOVED = 5,
+ MM_MODEM_ERROR_AUTHORIZATION_REQUIRED = 6,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET = 7
};
#define MM_MODEM_ERROR (mm_modem_error_quark ())
@@ -64,6 +68,7 @@ GType mm_modem_connect_error_get_type (void);
GError *mm_modem_connect_error_for_code (int error_code);
+/* 3GPP TS 07.07 version 7.8.0 Release 1998 (page 90) ETSI TS 100 916 V7.8.0 (2003-03) */
enum {
MM_MOBILE_ERROR_PHONE_FAILURE = 0,
MM_MOBILE_ERROR_NO_CONNECTION = 1,
@@ -115,8 +120,8 @@ enum {
MM_MOBILE_ERROR_GPRS_OPTION_NOT_SUPPORTED = 132,
MM_MOBILE_ERROR_GPRS_NOT_SUBSCRIBED = 133,
MM_MOBILE_ERROR_GPRS_OUT_OF_ORDER = 134,
- MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149,
MM_MOBILE_ERROR_GPRS_UNKNOWN = 148,
+ MM_MOBILE_ERROR_GPRS_PDP_AUTH_FAILURE = 149,
MM_MOBILE_ERROR_GPRS_INVALID_CLASS = 150
};
@@ -127,5 +132,6 @@ enum {
GQuark mm_mobile_error_quark (void);
GType mm_mobile_error_get_type (void);
GError *mm_mobile_error_for_code (int error_code);
+GError *mm_mobile_error_for_string (const char *str);
#endif /* MM_MODEM_ERROR_H */
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
index 50cd86c..9fe897f 100644
--- a/src/mm-generic-cdma.c
+++ b/src/mm-generic-cdma.c
@@ -23,10 +23,15 @@
#include "mm-generic-cdma.h"
#include "mm-modem-cdma.h"
#include "mm-modem-simple.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-serial-parsers.h"
+#include "mm-modem-helpers.h"
+#include "libqcdm/src/commands.h"
+
+#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state"
static void simple_reg_callback (MMModemCdma *modem,
MMModemCdmaRegistrationState cdma_1x_reg_state,
@@ -58,6 +63,10 @@ typedef struct {
gboolean evdo_rev0;
gboolean evdo_revA;
gboolean reg_try_css;
+ gboolean has_spservice;
+ gboolean has_speri;
+
+ guint poll_id;
MMModemCdmaRegistrationState cdma_1x_reg_state;
MMModemCdmaRegistrationState evdo_reg_state;
@@ -67,8 +76,9 @@ typedef struct {
guint reg_state_changed_id;
MMCallbackInfo *simple_connect_info;
- MMSerialPort *primary;
- MMSerialPort *secondary;
+ MMAtSerialPort *primary;
+ MMAtSerialPort *secondary;
+ MMQcdmSerialPort *qcdm;
MMPort *data;
} MMGenericCdmaPrivate;
@@ -114,6 +124,44 @@ check_valid (MMGenericCdma *self)
mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid);
}
+static void
+get_esn_cb (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_modem_base_set_equipment_identifier (MM_MODEM_BASE (modem), error ? "" : result);
+ mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_CDMA_GET_PRIVATE (modem)->primary));
+ check_valid (MM_GENERIC_CDMA (modem));
+ }
+}
+
+static void
+initial_esn_check (MMGenericCdma *self)
+{
+ GError *error = NULL;
+ MMGenericCdmaPrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_CDMA (self));
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ /* Make sure echoing is off */
+ mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL);
+ mm_modem_cdma_get_esn (MM_MODEM_CDMA (self), get_esn_cb, NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ check_valid (self);
+ }
+}
+
static gboolean
owns_port (MMModem *modem, const char *subsys, const char *name)
{
@@ -148,25 +196,36 @@ mm_generic_cdma_grab_port (MMGenericCdma *self,
}
port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
- if (port && MM_IS_SERIAL_PORT (port)) {
+ if (!port) {
+ g_warn_if_fail (port != NULL);
+ return NULL;
+ }
+
+ if (MM_IS_AT_SERIAL_PORT (port)) {
g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL);
- mm_serial_port_set_response_parser (MM_SERIAL_PORT (port),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
+ mm_serial_parser_v1_e1_parse,
+ mm_serial_parser_v1_e1_new (),
+ mm_serial_parser_v1_e1_destroy);
if (ptype == MM_PORT_TYPE_PRIMARY) {
- priv->primary = MM_SERIAL_PORT (port);
+ priv->primary = MM_AT_SERIAL_PORT (port);
if (!priv->data) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
}
- check_valid (self);
+
+ /* Get modem's ESN number */
+ initial_esn_check (self);
+
} else if (ptype == MM_PORT_TYPE_SECONDARY)
- priv->secondary = MM_SERIAL_PORT (port);
- } else {
+ priv->secondary = MM_AT_SERIAL_PORT (port);
+ } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
+ if (!priv->qcdm)
+ priv->qcdm = MM_QCDM_SERIAL_PORT (port);
+ } else if (!strcmp (subsys, "net")) {
/* Net device (if any) is the preferred data port */
- if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) {
+ if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
check_valid (self);
@@ -197,7 +256,7 @@ release_port (MMModem *modem, const char *subsys, const char *name)
if (!port)
return;
- if (port == MM_PORT (priv->primary)) {
+ if (port == (MMPort *) priv->primary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->primary = NULL;
}
@@ -207,17 +266,22 @@ release_port (MMModem *modem, const char *subsys, const char *name)
g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE);
}
- if (port == MM_PORT (priv->secondary)) {
+ if (port == (MMPort *) priv->secondary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->secondary = NULL;
}
+ if (port == (MMPort *) priv->qcdm) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->qcdm = NULL;
+ }
+
check_valid (MM_GENERIC_CDMA (modem));
}
-MMSerialPort *
-mm_generic_cdma_get_port (MMGenericCdma *modem,
- MMPortType ptype)
+MMAtSerialPort *
+mm_generic_cdma_get_at_port (MMGenericCdma *modem,
+ MMPortType ptype)
{
g_return_val_if_fail (MM_IS_GENERIC_CDMA (modem), NULL);
g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
@@ -230,6 +294,27 @@ mm_generic_cdma_get_port (MMGenericCdma *modem,
return NULL;
}
+MMAtSerialPort *
+mm_generic_cdma_get_best_at_port (MMGenericCdma *self, GError **error)
+{
+ MMGenericCdmaPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), NULL);
+
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+
+ if (!mm_port_get_connected (MM_PORT (priv->primary)))
+ return priv->primary;
+
+ if (!priv->secondary) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot perform this operation while connected");
+ }
+
+ return priv->secondary;
+}
+
/*****************************************************************************/
void
@@ -301,6 +386,38 @@ mm_generic_cdma_evdo_get_registration_state_sync (MMGenericCdma *self)
/*****************************************************************************/
static void
+periodic_poll_reg_cb (MMModemCdma *modem,
+ MMModemCdmaRegistrationState cdma_1x_reg_state,
+ MMModemCdmaRegistrationState evdo_reg_state,
+ GError *error,
+ gpointer user_data)
+{
+ /* cached reg state already updated */
+}
+
+static void
+periodic_poll_signal_quality_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ /* cached signal quality already updated */
+}
+
+static gboolean
+periodic_poll_cb (gpointer user_data)
+{
+ MMGenericCdma *self = MM_GENERIC_CDMA (user_data);
+
+ mm_modem_cdma_get_registration_state (MM_MODEM_CDMA (self), periodic_poll_reg_cb, NULL);
+ mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (self), periodic_poll_signal_quality_cb, NULL);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
update_enabled_state (MMGenericCdma *self,
gboolean stay_connected,
MMModemStateReason reason)
@@ -349,6 +466,43 @@ registration_cleanup (MMGenericCdma *self, GQuark error_class, guint32 error_num
}
static void
+get_enable_info_done (MMModem *modem,
+ const char *manufacturer,
+ const char *model,
+ const char *version,
+ GError *error,
+ gpointer user_data)
+{
+ /* Modem base class handles the response for us */
+}
+
+static void
+spservice_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error) {
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->has_spservice = TRUE;
+
+ /* +SPSERVICE provides a better indicator of registration status than
+ * +CSS, which some devices implement inconsistently.
+ */
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->reg_try_css = FALSE;
+ }
+}
+
+static void
+speri_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error)
+ MM_GENERIC_CDMA_GET_PRIVATE (user_data)->has_speri = TRUE;
+}
+
+static void
enable_all_done (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = user_data;
@@ -358,15 +512,33 @@ enable_all_done (MMModem *modem, GError *error, gpointer user_data)
if (error)
info->error = g_error_copy (error);
else {
+ /* Try to enable XON/XOFF flow control */
+ mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL);
+
/* Open up the second port, if one exists */
if (priv->secondary) {
- if (!mm_serial_port_open (priv->secondary, &info->error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &info->error)) {
+ g_assert (info->error);
+ goto out;
+ }
+ }
+
+ /* Open up the second port, if one exists */
+ if (priv->qcdm) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->qcdm), &info->error)) {
g_assert (info->error);
goto out;
}
}
update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
+
+ /* Grab device info right away */
+ mm_modem_get_info (modem, get_enable_info_done, NULL);
+
+ /* Check for support of Sprint-specific phone commands */
+ mm_at_serial_port_queue_command (priv->primary, "+SPSERVICE?", 3, spservice_done, self);
+ mm_at_serial_port_queue_command (priv->primary, "$SPERI?", 3, speri_done, self);
}
out:
@@ -380,7 +552,7 @@ out:
}
static void
-enable_error_reporting_done (MMSerialPort *port,
+enable_error_reporting_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -399,7 +571,7 @@ enable_error_reporting_done (MMSerialPort *port,
}
static void
-init_done (MMSerialPort *port,
+init_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -419,7 +591,7 @@ init_done (MMSerialPort *port,
FIXME: It's mandatory by spec, so it really shouldn't be optional. Figure
out which CDMA modems have problems with it and implement plugin for them.
*/
- mm_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data);
+ mm_at_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data);
}
}
@@ -439,7 +611,7 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data)
return;
}
- mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "Z E0 V1 X4 &C1", 3, init_done, user_data);
}
static void
@@ -453,7 +625,7 @@ enable (MMModem *modem,
info = mm_callback_info_new (modem, callback, user_data);
- if (!mm_serial_port_open (priv->primary, &info->error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &info->error)) {
g_assert (info->error);
mm_callback_info_schedule (info);
return;
@@ -463,7 +635,7 @@ enable (MMModem *modem,
MM_MODEM_STATE_ENABLING,
MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (priv->primary, 100, flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 100, FALSE, flash_done, info);
}
static void
@@ -489,7 +661,7 @@ disable_all_done (MMModem *modem, GError *error, gpointer user_data)
MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- mm_serial_port_close (priv->primary);
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary));
mm_modem_set_state (modem, MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_REASON_NONE);
priv->cdma_1x_reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
@@ -545,21 +717,24 @@ disable (MMModem *modem,
GUINT_TO_POINTER (state),
NULL);
+ /* Close auxiliary serial ports */
if (priv->secondary)
- mm_serial_port_close (priv->secondary);
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary));
+ if (priv->qcdm)
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->qcdm));
mm_modem_set_state (MM_MODEM (info->modem),
MM_MODEM_STATE_DISABLING,
MM_MODEM_STATE_REASON_NONE);
if (mm_port_get_connected (MM_PORT (priv->primary)))
- mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disable_flash_done, info);
else
- disable_flash_done (priv->primary, NULL, info);
+ disable_flash_done (MM_SERIAL_PORT (priv->primary), NULL, info);
}
static void
-dial_done (MMSerialPort *port,
+dial_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -598,7 +773,7 @@ connect (MMModem *modem,
info = mm_callback_info_new (modem, callback, user_data);
command = g_strconcat ("DT", number, NULL);
- mm_serial_port_queue_command (priv->primary, command, 90, dial_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 90, dial_done, info);
g_free (command);
}
@@ -648,83 +823,7 @@ disconnect (MMModem *modem,
NULL);
mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info);
-}
-
-static void
-card_info_invoke (MMCallbackInfo *info)
-{
- MMModemInfoFn callback = (MMModemInfoFn) info->callback;
-
- callback (info->modem,
- (char *) mm_callback_info_get_data (info, "card-info-manufacturer"),
- (char *) mm_callback_info_get_data (info, "card-info-model"),
- (char *) mm_callback_info_get_data (info, "card-info-version"),
- info->error, info->user_data);
-}
-
-static const char *
-strip_response (const char *resp, const char *cmd)
-{
- const char *p = resp;
-
- if (p) {
- if (!strncmp (p, cmd, strlen (cmd)))
- p += strlen (cmd);
- while (*p == ' ')
- p++;
- }
- return p;
-}
-
-static void
-get_version_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *p;
-
- if (!error) {
- p = strip_response (response->str, "+GMR:");
- mm_callback_info_set_data (info, "card-info-version", g_strdup (p), g_free);
- } else if (!info->error)
- info->error = g_error_copy (error);
-
- mm_callback_info_schedule (info);
-}
-
-static void
-get_model_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *p;
-
- if (!error) {
- p = strip_response (response->str, "+GMM:");
- mm_callback_info_set_data (info, "card-info-model", g_strdup (p), g_free);
- } else if (!info->error)
- info->error = g_error_copy (error);
-}
-
-static void
-get_manufacturer_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
-{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *p;
-
- if (!error) {
- p = strip_response (response->str, "+GMI:");
- mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (p), g_free);
- } else
- info->error = g_error_copy (error);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
}
static void
@@ -732,30 +831,12 @@ get_card_info (MMModem *modem,
MMModemInfoFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
- MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
-
- info = mm_callback_info_new_full (MM_MODEM (modem),
- card_info_invoke,
- G_CALLBACK (callback),
- user_data);
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot modem info while connected");
- mm_callback_info_schedule (info);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
- }
+ MMAtSerialPort *port;
+ GError *error = NULL;
- mm_serial_port_queue_command_cached (port, "+GMI", 3, get_manufacturer_done, info);
- mm_serial_port_queue_command_cached (port, "+GMM", 3, get_model_done, info);
- mm_serial_port_queue_command_cached (port, "+GMR", 3, get_version_done, info);
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &error);
+ mm_modem_base_get_card_info (MM_MODEM_BASE (modem), port, error, callback, user_data);
+ g_clear_error (&error);
}
/*****************************************************************************/
@@ -793,7 +874,7 @@ mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality)
#define CSQ2_TRIED "csq?-tried"
static void
-get_signal_quality_done (MMSerialPort *port,
+get_signal_quality_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -811,7 +892,7 @@ get_signal_quality_done (MMSerialPort *port,
* try the other command if the first one fails.
*/
mm_callback_info_set_data (info, CSQ2_TRIED, GUINT_TO_POINTER (1), NULL);
- mm_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info);
+ mm_at_serial_port_queue_command (port, "+CSQ?", 3, get_signal_quality_done, info);
return;
}
} else {
@@ -847,31 +928,102 @@ get_signal_quality_done (MMSerialPort *port,
}
static void
+qcdm_pilot_sets_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericCdmaPrivate *priv;
+ QCDMResult *result;
+ guint32 num = 0, quality = 0, i;
+ float best_db = -28;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+
+ /* Parse the response */
+ result = qcdm_cmd_pilot_sets_result ((const char *) response->data, response->len, &info->error);
+ if (!result)
+ goto done;
+
+ qcdm_cmd_pilot_sets_result_get_num (result, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE, &num);
+ for (i = 0; i < num; i++) {
+ guint32 pn_offset = 0, ecio = 0;
+ float db = 0;
+
+ qcdm_cmd_pilot_sets_result_get_pilot (result,
+ QCDM_CMD_PILOT_SETS_TYPE_ACTIVE,
+ i,
+ &pn_offset,
+ &ecio,
+ &db);
+ best_db = MAX (db, best_db);
+ }
+ qcdm_result_unref (result);
+
+ if (num > 0) {
+ #define BEST_ECIO 3
+ #define WORST_ECIO 25
+
+ /* EC/IO dB ranges from roughly 0 to -31 dB. Lower == worse. We
+ * really only care about -3 to -25 dB though, since that's about what
+ * you'll see in real-world usage.
+ */
+ best_db = CLAMP (ABS (best_db), BEST_ECIO, WORST_ECIO) - BEST_ECIO;
+ quality = (guint32) (100 - (best_db * 100 / (WORST_ECIO - BEST_ECIO)));
+ }
+
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
+
+ if (priv->cdma1x_quality != quality) {
+ priv->cdma1x_quality = quality;
+ mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (info->modem), quality);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
get_signal_quality (MMModemCdma *modem,
MMModemUIntFn callback,
gpointer user_data)
{
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
+ MMAtSerialPort *at_port;
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- g_message ("Returning saved signal quality %d", priv->cdma1x_quality);
- callback (MM_MODEM (modem), priv->cdma1x_quality, NULL, user_data);
- return;
- }
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- /* Use secondary port if primary is connected */
- port = priv->secondary;
+ at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error);
+ if (!at_port && !priv->qcdm) {
+ g_message ("Returning saved signal quality %d", priv->cdma1x_quality);
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->cdma1x_quality), NULL);
+ mm_callback_info_schedule (info);
+ return;
+ }
+ g_clear_error (&info->error);
+
+ if (at_port)
+ mm_at_serial_port_queue_command (at_port, "+CSQ", 3, get_signal_quality_done, info);
+ else if (priv->qcdm) {
+ GByteArray *pilot_sets;
+
+ /* Use CDMA1x pilot EC/IO if we can */
+ pilot_sets = g_byte_array_sized_new (25);
+ pilot_sets->len = qcdm_cmd_pilot_sets_new ((char *) pilot_sets->data, 25, NULL);
+ g_assert (pilot_sets->len);
+ mm_qcdm_serial_port_queue_command (priv->qcdm, pilot_sets, 3, qcdm_pilot_sets_cb, info);
}
-
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command (port, "+CSQ", 3, get_signal_quality_done, info);
}
static void
-get_string_done (MMSerialPort *port,
+get_string_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -882,7 +1034,7 @@ get_string_done (MMSerialPort *port,
if (error)
info->error = g_error_copy (error);
else {
- p = strip_response (response->str, "+GSN:");
+ p = mm_strip_tag (response->str, "+GSN:");
mm_callback_info_set_result (info, g_strdup (p), g_free);
}
@@ -894,26 +1046,18 @@ get_esn (MMModemCdma *modem,
MMModemStringFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
- GError *error;
- MMSerialPort *port = priv->primary;
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get ESN while connected");
- callback (MM_MODEM (modem), NULL, error, user_data);
- g_error_free (error);
- return;
- }
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- /* Use secondary port if primary is connected */
- port = priv->secondary;
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
}
- info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+GSN", 3, get_string_done, info);
}
static void
@@ -996,8 +1140,17 @@ convert_sid (const char *sid)
return (int) tmp_sid;
}
+static GError *
+new_css_no_service_error (void)
+{
+ /* NOTE: update reg_state_css_response() if this error changes */
+ return g_error_new_literal (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_NO_NETWORK,
+ "No service");
+}
+
static void
-serving_system_done (MMSerialPort *port,
+serving_system_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1089,12 +1242,9 @@ serving_system_done (MMSerialPort *port,
sid = 99999;
/* 99999 means unknown/no service */
- if (sid == 99999) {
- /* NOTE: update reg_state_css_response() if this error changes */
- info->error = g_error_new_literal (MM_MOBILE_ERROR,
- MM_MOBILE_ERROR_NO_NETWORK,
- "No service");
- } else {
+ if (sid == 99999)
+ info->error = new_css_no_service_error ();
+ else {
mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (class), NULL);
mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) band), NULL);
mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL);
@@ -1109,39 +1259,97 @@ serving_system_done (MMSerialPort *port,
}
static void
+legacy_get_serving_system (MMGenericCdma *self, MMCallbackInfo *info)
+{
+ MMAtSerialPort *port;
+
+ port = mm_generic_cdma_get_best_at_port (self, &info->error);
+ if (port)
+ mm_at_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info);
+ else
+ mm_callback_info_schedule (info);
+}
+
+static void
+cdma_status_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ QCDMResult *result;
+ guint32 sid, rxstate;
+
+ if (error)
+ goto error;
+
+ /* Parse the response */
+ result = qcdm_cmd_cdma_status_result ((const char *) response->data, response->len, &info->error);
+ if (!result)
+ goto error;
+
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, &rxstate);
+ qcdm_result_get_uint32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, &sid);
+ qcdm_result_unref (result);
+
+ if (rxstate == QCDM_CMD_CDMA_STATUS_RX_STATE_ENTERING_CDMA)
+ info->error = new_css_no_service_error ();
+ else {
+ mm_callback_info_set_data (info, "class", GUINT_TO_POINTER (0), NULL);
+ mm_callback_info_set_data (info, "band", GUINT_TO_POINTER ((guint32) 'Z'), NULL);
+ mm_callback_info_set_data (info, "sid", GUINT_TO_POINTER (sid), NULL);
+ }
+
+ mm_callback_info_schedule (info);
+ return;
+
+error:
+ /* If there was some error, fall back to use +CSS like we did before QCDM */
+ legacy_get_serving_system (MM_GENERIC_CDMA (info->modem), info);
+}
+
+static void
get_serving_system (MMModemCdma *modem,
MMModemCdmaServingSystemFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
+ MMGenericCdma *self = MM_GENERIC_CDMA (modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
MMCallbackInfo *info;
- GError *error;
- MMSerialPort *port = priv->primary;
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get serving system while connected");
- callback (modem, 0, 0, 0, error, user_data);
- g_error_free (error);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
- }
info = mm_callback_info_new_full (MM_MODEM (modem),
serving_system_invoke,
G_CALLBACK (callback),
user_data);
- mm_serial_port_queue_command (port, "+CSS?", 3, serving_system_done, info);
+ if (priv->qcdm) {
+ GByteArray *cdma_status;
+
+ cdma_status = g_byte_array_sized_new (25);
+ cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25, NULL);
+ g_assert (cdma_status->len);
+ mm_qcdm_serial_port_queue_command (priv->qcdm, cdma_status, 3, cdma_status_cb, info);
+ } else
+ legacy_get_serving_system (self, info);
}
+/*****************************************************************************/
+
+/* Registration state stuff */
+
#define CDMA_1X_STATE_TAG "cdma-1x-reg-state"
#define EVDO_STATE_TAG "evdo-reg-state"
+MMModemCdmaRegistrationState
+mm_generic_cdma_query_reg_state_get_callback_1x_state (MMCallbackInfo *info)
+{
+ g_return_val_if_fail (info != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (info->modem != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_CDMA (info->modem), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+
+ return GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG));
+}
+
void
mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
@@ -1153,6 +1361,16 @@ mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info,
mm_callback_info_set_data (info, CDMA_1X_STATE_TAG, GUINT_TO_POINTER (new_state), NULL);
}
+MMModemCdmaRegistrationState
+mm_generic_cdma_query_reg_state_get_callback_evdo_state (MMCallbackInfo *info)
+{
+ g_return_val_if_fail (info != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (info->modem != NULL, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_CDMA (info->modem), MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+
+ return GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG));
+}
+
void
mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
@@ -1171,18 +1389,19 @@ registration_state_invoke (MMCallbackInfo *info)
/* note: This is the MMModemCdma interface callback */
callback (MM_MODEM_CDMA (info->modem),
- GPOINTER_TO_UINT (mm_callback_info_get_data (info, CDMA_1X_STATE_TAG)),
- GPOINTER_TO_UINT (mm_callback_info_get_data (info, EVDO_STATE_TAG)),
+ mm_generic_cdma_query_reg_state_get_callback_1x_state (info),
+ mm_generic_cdma_query_reg_state_get_callback_evdo_state (info),
info->error,
info->user_data);
}
MMCallbackInfo *
mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
- MMGenericCdmaPrivate *priv;
MMCallbackInfo *info;
g_return_val_if_fail (self != NULL, NULL);
@@ -1195,15 +1414,8 @@ mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self,
user_data);
/* Fill with current state */
- priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- mm_callback_info_set_data (info,
- CDMA_1X_STATE_TAG,
- GUINT_TO_POINTER (priv->cdma_1x_reg_state),
- NULL);
- mm_callback_info_set_data (info,
- EVDO_STATE_TAG,
- GUINT_TO_POINTER (priv->evdo_reg_state),
- NULL);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, cur_cdma_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, cur_evdo_state);
return info;
}
@@ -1211,60 +1423,138 @@ static void
set_callback_1x_state_helper (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
{
- MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+ if (info->modem) {
+ MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
- mm_generic_cdma_set_1x_registration_state (self, new_state);
- mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state);
+ mm_generic_cdma_set_1x_registration_state (self, new_state);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state);
+ }
}
static void
set_callback_evdo_state_helper (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state)
{
- MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
- MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
+ if (info->modem) {
+ MMGenericCdma *self = MM_GENERIC_CDMA (info->modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem);
- mm_generic_cdma_set_evdo_registration_state (self, new_state);
- mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state);
+ mm_generic_cdma_set_evdo_registration_state (self, new_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state);
+ }
}
static void
-reg_state_query_done (MMModemCdma *cdma,
- MMModemCdmaRegistrationState cdma_1x_reg_state,
- MMModemCdmaRegistrationState evdo_reg_state,
+subclass_reg_query_done (MMModemCdma *cdma,
+ MMModemCdmaRegistrationState cdma_reg_state,
+ MMModemCdmaRegistrationState evdo_reg_state,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ /* Set final registration state */
+ set_callback_1x_state_helper (info, cdma_reg_state);
+ set_callback_evdo_state_helper (info, evdo_reg_state);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_query_speri_done (MMAtSerialPort *port,
+ GString *response,
GError *error,
gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMCallbackInfo *info = user_data;
+ gboolean roam = FALSE;
+ const char *p;
+
+ if (error)
+ goto done;
+
+ p = mm_strip_tag (response->str, "$SPERI:");
+ if (!p || !mm_cdma_parse_eri (p, &roam, NULL, NULL))
+ goto done;
+
+ /* Change the 1x and EVDO registration states to roaming if they were
+ * anything other than UNKNOWN.
+ */
+ if (roam) {
+ if (mm_generic_cdma_query_reg_state_get_callback_1x_state (info))
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+
+ if (mm_generic_cdma_query_reg_state_get_callback_evdo_state (info))
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_query_spservice_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
if (error)
info->error = g_error_copy (error);
- else {
- set_callback_1x_state_helper (info, cdma_1x_reg_state);
- set_callback_evdo_state_helper (info, evdo_reg_state);
+ else if (mm_cdma_parse_spservice_response (response->str, &cdma_state, &evdo_state)) {
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, cdma_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, evdo_state);
+
+ if (MM_GENERIC_CDMA_GET_PRIVATE (info->modem)->has_speri) {
+ /* Get roaming status to override generic registration state */
+ mm_at_serial_port_queue_command (port, "$SPERI?", 3, reg_query_speri_done, info);
+ return;
+ }
}
mm_callback_info_schedule (info);
}
static void
-query_subclass_registration_state (MMGenericCdma *self, MMCallbackInfo *info)
+real_query_registration_state (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
+ MMModemCdmaRegistrationStateFn callback,
+ gpointer user_data)
{
- /* Let subclasses figure out roaming and detailed registration state */
- if (MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state) {
- MM_GENERIC_CDMA_GET_CLASS (self)->query_registration_state (self,
- reg_state_query_done,
- info);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ /* Seed this CallbackInfo with any previously determined registration state */
+ info = mm_generic_cdma_query_reg_state_callback_info_new (self,
+ cur_cdma_state,
+ cur_evdo_state,
+ callback,
+ user_data);
+
+ port = mm_generic_cdma_get_best_at_port (self, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ if (MM_GENERIC_CDMA_GET_PRIVATE (self)->has_spservice) {
+ /* Try Sprint-specific commands */
+ mm_at_serial_port_queue_command (port, "+SPSERVICE?", 3, reg_query_spservice_done, info);
} else {
- /* Or if the subclass doesn't implement more specific checking,
- * assume we're registered.
+ /* Assume we're registered on the 1x network if we passed +CAD, +CSS,
+ * and QCDM Call Manager checking.
*/
- reg_state_query_done (MM_MODEM_CDMA (self),
- MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
- MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
- NULL,
- info);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
+ mm_callback_info_schedule (info);
}
}
@@ -1282,8 +1572,7 @@ reg_state_css_response (MMModemCdma *cdma,
* report unknown registration state.
*/
if (error) {
- if ( (error->domain == MM_MOBILE_ERROR)
- && (error->code == MM_MOBILE_ERROR_NO_NETWORK)) {
+ if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_NO_NETWORK)) {
set_callback_1x_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
set_callback_evdo_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
} else {
@@ -1291,14 +1580,20 @@ reg_state_css_response (MMModemCdma *cdma,
info->error = g_error_copy (error);
}
mm_callback_info_schedule (info);
- return;
+ } else {
+ /* We're registered on the CDMA 1x network at least, but let subclasses
+ * do more specific registration checking.
+ */
+ MM_GENERIC_CDMA_GET_CLASS (cdma)->query_registration_state (MM_GENERIC_CDMA (info->modem),
+ MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ subclass_reg_query_done,
+ info);
}
-
- query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info);
}
static void
-get_analog_digital_done (MMSerialPort *port,
+get_analog_digital_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1313,7 +1608,7 @@ get_analog_digital_done (MMSerialPort *port,
}
/* Strip any leading command tag and spaces */
- reply = strip_response (response->str, "+CAD:");
+ reply = mm_strip_tag (response->str, "+CAD:");
errno = 0;
int_cad = strtol (reply, NULL, 10);
@@ -1345,7 +1640,11 @@ get_analog_digital_done (MMSerialPort *port,
/* Subclass knows that AT+CSS? will respond incorrectly to EVDO
* state, so skip AT+CSS? query.
*/
- query_subclass_registration_state (MM_GENERIC_CDMA (info->modem), info);
+ MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state (MM_GENERIC_CDMA (info->modem),
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ subclass_reg_query_done,
+ info);
}
return;
} else {
@@ -1359,28 +1658,113 @@ error:
}
static void
+reg_cmstate_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMAtSerialPort *at_port;
+ QCDMResult *result;
+ guint32 opmode = 0, sysmode = 0;
+ MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+
+ if (error)
+ goto error;
+
+ /* Parse the response */
+ result = qcdm_cmd_cm_subsys_state_info_result ((const char *) response->data, response->len, &info->error);
+ if (!result)
+ goto error;
+
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &opmode);
+ qcdm_result_get_uint32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &sysmode);
+ qcdm_result_unref (result);
+
+ if (opmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) {
+ switch (sysmode) {
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA:
+ cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_HDR:
+ evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_AMPS:
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE:
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA:
+ default:
+ break;
+ }
+
+ if (cdma_state || evdo_state) {
+ /* Device is registered to something; see if the subclass has a
+ * better idea of whether we're roaming or not and what the
+ * access technology is.
+ */
+ if (MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state) {
+ MM_GENERIC_CDMA_GET_CLASS (info->modem)->query_registration_state (MM_GENERIC_CDMA (info->modem),
+ cdma_state,
+ evdo_state,
+ subclass_reg_query_done,
+ info);
+ return;
+ }
+ }
+ }
+
+ set_callback_1x_state_helper (info, cdma_state);
+ set_callback_evdo_state_helper (info, evdo_state);
+ mm_callback_info_schedule (info);
+ return;
+
+error:
+ /* If there was some error, fall back to use +CAD like we did before QCDM */
+ at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (info->modem), &info->error);
+ if (at_port)
+ mm_at_serial_port_queue_command (at_port, "+CAD?", 3, get_analog_digital_done, info);
+ else
+ mm_callback_info_schedule (info);
+}
+
+static void
get_registration_state (MMModemCdma *modem,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data)
{
MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- g_message ("Returning saved registration states: 1x: %d EVDO: %d",
- priv->cdma_1x_reg_state, priv->evdo_reg_state);
- callback (MM_MODEM_CDMA (modem), priv->cdma_1x_reg_state, priv->evdo_reg_state, NULL, user_data);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
+ MMAtSerialPort *port;
+
+ info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem),
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+ callback,
+ user_data);
+
+ port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error);
+ if (!port && !priv->qcdm) {
+ g_message ("Returning saved registration states: 1x: %d EVDO: %d",
+ priv->cdma_1x_reg_state, priv->evdo_reg_state);
+ mm_generic_cdma_query_reg_state_set_callback_1x_state (info, priv->cdma_1x_reg_state);
+ mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, priv->evdo_reg_state);
+ mm_callback_info_schedule (info);
+ return;
}
- info = mm_generic_cdma_query_reg_state_callback_info_new (MM_GENERIC_CDMA (modem), callback, user_data);
- mm_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info);
+ /* Use QCDM for Call Manager state or HDR state before trying CAD, since
+ * CAD doesn't always reflect the state of the HDR radio's registration
+ * status.
+ */
+ if (priv->qcdm) {
+ GByteArray *cmstate;
+
+ cmstate = g_byte_array_sized_new (25);
+ cmstate->len = qcdm_cmd_cm_subsys_state_info_new ((char *) cmstate->data, 25, NULL);
+ g_assert (cmstate->len);
+ mm_qcdm_serial_port_queue_command (priv->qcdm, cmstate, 3, reg_cmstate_cb, info);
+ } else
+ mm_at_serial_port_queue_command (port, "+CAD?", 3, get_analog_digital_done, info);
}
/*****************************************************************************/
@@ -1512,10 +1896,9 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
const char *str;
guint id;
- if (error) {
- info->error = g_error_copy (error);
+ info->error = mm_modem_check_removed (modem, error);
+ if (info->error)
goto out;
- }
switch (state) {
case SIMPLE_STATE_BEGIN:
@@ -1550,7 +1933,8 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
out:
if (info->error || state == SIMPLE_STATE_DONE) {
- registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0);
+ if (modem)
+ registration_cleanup (MM_GENERIC_CDMA (modem), 0, 0);
mm_callback_info_schedule (info);
}
}
@@ -1609,16 +1993,26 @@ simple_uint_value (guint32 i)
return val;
}
+#define SS_HASH_TAG "simple-get-status"
+
static void
simple_status_got_signal_quality (MMModem *modem,
guint32 result,
GError *error,
gpointer user_data)
{
- if (error)
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ if (error) {
+ info->error = g_error_copy (error);
g_warning ("Error getting signal quality: %s", error->message);
- else
- g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result));
+ } else {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "signal_quality", simple_uint_value (result));
+ }
+
+ mm_callback_info_schedule (info);
}
static void
@@ -1627,7 +2021,7 @@ simple_get_status_invoke (MMCallbackInfo *info)
MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback;
callback (MM_MODEM_SIMPLE (info->modem),
- (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"),
+ (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG),
info->error, info->user_data);
}
@@ -1644,9 +2038,9 @@ simple_get_status (MMModemSimple *simple,
G_CALLBACK (callback),
user_data);
- properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue);
- mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref);
- mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, properties);
+ properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue);
+ mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref);
+ mm_modem_cdma_get_signal_quality (MM_MODEM_CDMA (simple), simple_status_got_signal_quality, info);
}
/*****************************************************************************/
@@ -1659,6 +2053,28 @@ modem_valid_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data)
registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL);
}
+static void
+modem_state_changed (MMGenericCdma *self, GParamSpec *pspec, gpointer user_data)
+{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+ MMModemState state;
+
+ /* Start polling registration status and signal quality when enabled */
+
+ state = mm_modem_get_state (MM_MODEM (self));
+ if (state >= MM_MODEM_STATE_ENABLED) {
+ if (!priv->poll_id) {
+ priv->poll_id = g_timeout_add_seconds (30, periodic_poll_cb, self);
+ /* Kick one off immediately */
+ periodic_poll_cb (self);
+ }
+ } else {
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+}
+
/*****************************************************************************/
static void
@@ -1690,27 +2106,13 @@ modem_simple_init (MMModemSimple *class)
class->get_status = simple_get_status;
}
-static GObject*
-constructor (GType type,
- guint n_construct_params,
- GObjectConstructParam *construct_params)
-{
- GObject *object;
-
- object = G_OBJECT_CLASS (mm_generic_cdma_parent_class)->constructor (type,
- n_construct_params,
- construct_params);
- if (object) {
- g_signal_connect (object, "notify::" MM_MODEM_VALID,
- G_CALLBACK (modem_valid_changed), NULL);
- }
-
- return object;
-}
-
static void
mm_generic_cdma_init (MMGenericCdma *self)
{
+ g_signal_connect (self, "notify::" MM_MODEM_VALID,
+ G_CALLBACK (modem_valid_changed), NULL);
+ g_signal_connect (self, "notify::" MM_MODEM_STATE,
+ G_CALLBACK (modem_state_changed), NULL);
}
static void
@@ -1771,15 +2173,15 @@ get_property (GObject *object, guint prop_id,
static void
dispose (GObject *object)
{
- registration_cleanup (MM_GENERIC_CDMA (object), MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL);
+ MMGenericCdma *self = MM_GENERIC_CDMA (object);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
- G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object);
-}
+ registration_cleanup (self, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL);
-static void
-finalize (GObject *object)
-{
- G_OBJECT_CLASS (mm_generic_cdma_parent_class)->finalize (object);
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+
+ G_OBJECT_CLASS (mm_generic_cdma_parent_class)->dispose (object);
}
static void
@@ -1794,8 +2196,7 @@ mm_generic_cdma_class_init (MMGenericCdmaClass *klass)
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->dispose = dispose;
- object_class->finalize = finalize;
- object_class->constructor = constructor;
+ klass->query_registration_state = real_query_registration_state;
/* Properties */
g_object_class_override_property (object_class,
diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h
index 5b4a0b6..e4f9e57 100644
--- a/src/mm-generic-cdma.h
+++ b/src/mm-generic-cdma.h
@@ -20,7 +20,7 @@
#include "mm-modem.h"
#include "mm-modem-base.h"
#include "mm-modem-cdma.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
#include "mm-callback-info.h"
#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ())
@@ -42,7 +42,27 @@ typedef struct {
typedef struct {
MMModemBaseClass parent;
+ /* Subclasses should implement this function if they can more accurately
+ * determine the registration state and/or roaming status than the base
+ * class can (by using manufacturer custom AT commands or whatever).
+ * The base class passes its detected registration state in the
+ * cur_cdma_state and cur_evdo_state arguments, which the subclass should
+ * override if necessary before passing to the callback.
+ *
+ * Subclasses can use the helper functions
+ * mm_generic_cdma_query_reg_state_callback_info_new(),
+ * mm_generic_cdma_query_reg_state_set_callback_1x_state(), and
+ * mm_generic_cdma_query_reg_state_set_callback_evdo_state() to create the
+ * MMCallbackInfo object and to set the registration state which is passed
+ * to the callback when the subclass' registration query completes.
+ *
+ * Subclasses should generally not return parsing or other non-critical
+ * errors to the callback since that fails the entire registration check,
+ * rendering the superclass' checks useless.
+ */
void (*query_registration_state) (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data);
@@ -71,8 +91,6 @@ MMModem *mm_generic_cdma_new (const char *device,
/* Private, for subclasses */
-#define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state"
-
MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self,
const char *subsys,
const char *name,
@@ -80,7 +98,10 @@ MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self,
gpointer user_data,
GError **error);
-MMSerialPort *mm_generic_cdma_get_port (MMGenericCdma *modem, MMPortType ptype);
+MMAtSerialPort *mm_generic_cdma_get_at_port (MMGenericCdma *modem, MMPortType ptype);
+
+MMAtSerialPort *mm_generic_cdma_get_best_at_port (MMGenericCdma *modem,
+ GError **error);
void mm_generic_cdma_update_cdma1x_quality (MMGenericCdma *self, guint32 quality);
void mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality);
@@ -99,12 +120,18 @@ MMModemCdmaRegistrationState mm_generic_cdma_evdo_get_registration_state_sync (M
/* query_registration_state class function helpers */
MMCallbackInfo *mm_generic_cdma_query_reg_state_callback_info_new (MMGenericCdma *self,
+ MMModemCdmaRegistrationState cur_cdma_state,
+ MMModemCdmaRegistrationState cur_evdo_state,
MMModemCdmaRegistrationStateFn callback,
gpointer user_data);
+MMModemCdmaRegistrationState mm_generic_cdma_query_reg_state_get_callback_1x_state (MMCallbackInfo *info);
+
void mm_generic_cdma_query_reg_state_set_callback_1x_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state);
+MMModemCdmaRegistrationState mm_generic_cdma_query_reg_state_get_callback_evdo_state (MMCallbackInfo *info);
+
void mm_generic_cdma_query_reg_state_set_callback_evdo_state (MMCallbackInfo *info,
MMModemCdmaRegistrationState new_state);
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 4954ca1..08cde10 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -12,13 +12,15 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
- * Copyright (C) 2009 Ericsson
+ * Copyright (C) 2009 - 2010 Ericsson
*/
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+
#include "mm-generic-gsm.h"
#include "mm-modem-gsm-card.h"
#include "mm-modem-gsm-network.h"
@@ -26,8 +28,13 @@
#include "mm-modem-simple.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-modem-helpers.h"
+#include "mm-options.h"
+#include "mm-properties-changed-signal.h"
+#include "mm-utils.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -50,39 +57,88 @@ typedef struct {
char *device;
gboolean valid;
+ gboolean pin_checked;
+ guint32 pin_check_tries;
+ guint pin_check_timeout;
+
+ MMModemGsmAllowedMode allowed_mode;
+
+ gboolean roam_allowed;
char *oper_code;
char *oper_name;
guint32 ip_method;
- gboolean unsolicited_registration;
- MMModemGsmNetworkRegStatus reg_status;
+ GPtrArray *reg_regex;
+
+ guint poll_id;
+
+ /* CREG and CGREG info */
+ gboolean creg_poll;
+ gboolean cgreg_poll;
+ /* Index 0 for CREG, index 1 for CGREG */
+ gulong lac[2];
+ gulong cell_id[2];
+ MMModemGsmAccessTech act;
+
+ /* Index 0 for CREG, index 1 for CGREG */
+ MMModemGsmNetworkRegStatus reg_status[2];
guint pending_reg_id;
MMCallbackInfo *pending_reg_info;
+ gboolean manual_reg;
+ guint signal_quality_id;
+ time_t signal_quality_timestamp;
guint32 signal_quality;
- guint32 cid;
+ gint cid;
+
+ guint32 charsets;
+ guint32 cur_charset;
- MMSerialPort *primary;
- MMSerialPort *secondary;
+ MMAtSerialPort *primary;
+ MMAtSerialPort *secondary;
+ MMQcdmSerialPort *qcdm;
MMPort *data;
} MMGenericGsmPrivate;
-static void get_registration_status (MMSerialPort *port, MMCallbackInfo *info);
-static void read_operator_code_done (MMSerialPort *port,
+static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info);
+static void read_operator_code_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data);
-static void read_operator_name_done (MMSerialPort *port,
+static void read_operator_name_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data);
-static void reg_state_changed (MMSerialPort *port,
+static void reg_state_changed (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data);
+static void get_reg_status_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static gboolean handle_reg_status_response (MMGenericGsm *self,
+ GString *response,
+ GError **error);
+
+static MMModemGsmAccessTech etsi_act_to_mm_act (gint act);
+
+static void _internal_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act);
+
+static void reg_info_updated (MMGenericGsm *self,
+ gboolean update_rs,
+ MMGenericGsmRegType rs_type,
+ MMModemGsmNetworkRegStatus status,
+ gboolean update_code,
+ const char *oper_code,
+ gboolean update_name,
+ const char *oper_name);
+
MMModem *
mm_generic_gsm_new (const char *device,
const char *driver,
@@ -99,24 +155,7 @@ mm_generic_gsm_new (const char *device,
NULL));
}
-void
-mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
- gboolean enabled)
-{
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- MM_GENERIC_GSM_GET_PRIVATE (modem)->unsolicited_registration = enabled;
-}
-
-void
-mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid)
-{
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- MM_GENERIC_GSM_GET_PRIVATE (modem)->cid = cid;
-}
-
-guint32
+gint
mm_generic_gsm_get_cid (MMGenericGsm *modem)
{
g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), 0);
@@ -124,93 +163,98 @@ mm_generic_gsm_get_cid (MMGenericGsm *modem)
return MM_GENERIC_GSM_GET_PRIVATE (modem)->cid;
}
-static void
-got_signal_quality (MMModem *modem,
- guint32 result,
- GError *error,
- gpointer user_data)
-{
-}
-
-void
-mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
- MMModemGsmNetworkRegStatus status)
-{
- MMGenericGsmPrivate *priv;
-
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
-
- if (priv->reg_status != status) {
- priv->reg_status = status;
-
- g_debug ("Registration state changed: %d", status);
-
- if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
- status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
- mm_serial_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem);
- mm_serial_port_queue_command (priv->primary, "+COPS=3,0;+COPS?", 3, read_operator_name_done, modem);
- mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL);
- } else {
- g_free (priv->oper_code);
- g_free (priv->oper_name);
- priv->oper_code = priv->oper_name = NULL;
+typedef struct {
+ const char *result;
+ const char *normalized;
+ guint code;
+} CPinResult;
- mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (modem), priv->reg_status,
- priv->oper_code, priv->oper_name);
- }
+static CPinResult unlock_results[] = {
+ /* Longer entries first so we catch the correct one with strcmp() */
+ { "PH-NETSUB PIN", "ph-netsub-pin", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
+ { "PH-NETSUB PUK", "ph-netsub-puk", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
+ { "PH-FSIM PIN", "ph-fsim-pin", MM_MOBILE_ERROR_PH_FSIM_PIN },
+ { "PH-FSIM PUK", "ph-fsim-puk", MM_MOBILE_ERROR_PH_FSIM_PUK },
+ { "PH-CORP PIN", "ph-corp-pin", MM_MOBILE_ERROR_CORP_PIN },
+ { "PH-CORP PUK", "ph-corp-puk", MM_MOBILE_ERROR_CORP_PUK },
+ { "PH-SIM PIN", "ph-sim-pin", MM_MOBILE_ERROR_PH_SIM_PIN },
+ { "PH-NET PIN", "ph-net-pin", MM_MOBILE_ERROR_NETWORK_PIN },
+ { "PH-NET PUK", "ph-net-puk", MM_MOBILE_ERROR_NETWORK_PUK },
+ { "PH-SP PIN", "ph-sp-pin", MM_MOBILE_ERROR_SERVICE_PIN },
+ { "PH-SP PUK", "ph-sp-puk", MM_MOBILE_ERROR_SERVICE_PUK },
+ { "SIM PIN2", "sim-pin2", MM_MOBILE_ERROR_SIM_PIN2 },
+ { "SIM PUK2", "sim-puk2", MM_MOBILE_ERROR_SIM_PUK2 },
+ { "SIM PIN", "sim-pin", MM_MOBILE_ERROR_SIM_PIN },
+ { "SIM PUK", "sim-puk", MM_MOBILE_ERROR_SIM_PUK },
+ { NULL, NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
+};
+
+static GError *
+error_for_unlock_required (const char *unlock)
+{
+ CPinResult *iter = &unlock_results[0];
+
+ if (!unlock || !strlen (unlock))
+ return NULL;
- mm_generic_gsm_update_enabled_state (modem, TRUE, MM_MODEM_STATE_REASON_NONE);
+ /* Translate the error */
+ while (iter->result) {
+ if (!strcmp (iter->normalized, unlock))
+ return mm_mobile_error_for_code (iter->code);
+ iter++;
}
+
+ return g_error_new (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_UNKNOWN,
+ "Unknown unlock request '%s'", unlock);
}
-typedef struct {
- const char *result;
- guint code;
-} CPinResult;
+static void
+get_unlock_retries_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ if (!error)
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), result);
+ else
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (modem), MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED);
+}
static void
-pin_check_done (MMSerialPort *port,
+pin_check_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean parsed = FALSE;
- static CPinResult results[] = {
- { "SIM PIN", MM_MOBILE_ERROR_SIM_PIN },
- { "SIM PUK", MM_MOBILE_ERROR_SIM_PUK },
- { "PH-SIM PIN", MM_MOBILE_ERROR_PH_SIM_PIN },
- { "PH-FSIM PIN", MM_MOBILE_ERROR_PH_FSIM_PIN },
- { "PH-FSIM PUK", MM_MOBILE_ERROR_PH_FSIM_PUK },
- { "SIM PIN2", MM_MOBILE_ERROR_SIM_PIN2 },
- { "SIM PUK2", MM_MOBILE_ERROR_SIM_PUK2 },
- { "PH-NET PIN", MM_MOBILE_ERROR_NETWORK_PIN },
- { "PH-NET PUK", MM_MOBILE_ERROR_NETWORK_PUK },
- { "PH-NETSUB PIN", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
- { "PH-NETSUB PUK", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
- { "PH-SP PIN", MM_MOBILE_ERROR_SERVICE_PIN },
- { "PH-SP PUK", MM_MOBILE_ERROR_SERVICE_PUK },
- { "PH-CORP PIN", MM_MOBILE_ERROR_CORP_PIN },
- { "PH-CORP PUK", MM_MOBILE_ERROR_CORP_PUK },
- { NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
- };
if (error)
info->error = g_error_copy (error);
- else if (g_str_has_prefix (response->str, "+CPIN: ")) {
- const char *str = response->str + 7;
+ else if (response && strstr (response->str, "+CPIN: ")) {
+ const char *str = strstr (response->str, "+CPIN: ") + 7;
- if (g_str_has_prefix (str, "READY"))
+ if (g_str_has_prefix (str, "READY")) {
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+ if (MM_MODEM_GSM_CARD_GET_INTERFACE (info->modem)->get_unlock_retries)
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
+ else
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem),
+ MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED);
parsed = TRUE;
- else {
- CPinResult *iter = &results[0];
+ } else {
+ CPinResult *iter = &unlock_results[0];
/* Translate the error */
while (iter->result) {
if (g_str_has_prefix (str, iter->result)) {
info->error = mm_mobile_error_for_code (iter->code);
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), iter->normalized);
+ mm_modem_gsm_card_get_unlock_retries (MM_MODEM_GSM_CARD (info->modem),
+ iter->normalized,
+ get_unlock_retries_cb,
+ NULL);
parsed = TRUE;
break;
}
@@ -219,20 +263,26 @@ pin_check_done (MMSerialPort *port,
}
}
- if (!info->error && !parsed) {
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse PIN request response '%s'",
- response->str);
+ if (!parsed) {
+ /* Assume unlocked if we don't recognize the pin request result */
+ mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
+ mm_modem_base_set_unlock_retries (MM_MODEM_BASE (info->modem), 0);
+
+ if (!info->error) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse PIN request response '%s'",
+ response->str);
+ }
}
mm_callback_info_schedule (info);
}
-void
-mm_generic_gsm_check_pin (MMGenericGsm *modem,
- MMModemFn callback,
- gpointer user_data)
+static void
+check_pin (MMGenericGsm *modem,
+ MMModemFn callback,
+ gpointer user_data)
{
MMGenericGsmPrivate *priv;
MMCallbackInfo *info;
@@ -241,25 +291,66 @@ mm_generic_gsm_check_pin (MMGenericGsm *modem,
priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
+ mm_at_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
+}
+
+static void
+get_imei_cb (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_modem_base_set_equipment_identifier (MM_MODEM_BASE (modem), error ? "" : result);
+ mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_GSM_GET_PRIVATE (modem)->primary));
+ }
}
/*****************************************************************************/
+static MMModemGsmNetworkRegStatus
+gsm_reg_status (MMGenericGsm *self)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ /* Some devices (Blackberries for example) will respond to +CGREG, but
+ * return ERROR for +CREG, probably because their firmware is just stupid.
+ * So here we prefer the +CREG response, but if we never got a successful
+ * +CREG response, we'll take +CGREG instead.
+ */
+
+ if ( priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)
+ return priv->reg_status[0];
+
+ if ( priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)
+ return priv->reg_status[1];
+
+ if (priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING)
+ return priv->reg_status[0];
+
+ if (priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING)
+ return priv->reg_status[1];
+
+ if (priv->reg_status[0] != MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN)
+ return priv->reg_status[0];
+
+ return priv->reg_status[1];
+}
+
void
mm_generic_gsm_update_enabled_state (MMGenericGsm *self,
gboolean stay_connected,
MMModemStateReason reason)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
-
/* While connected we don't want registration status changes to change
* the modem's state away from CONNECTED.
*/
if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING))
return;
- switch (priv->reg_status) {
+ switch (gsm_reg_status (self)) {
case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_REGISTERED, reason);
@@ -282,12 +373,109 @@ check_valid (MMGenericGsm *self)
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
gboolean new_valid = FALSE;
- if (priv->primary && priv->data)
+ if (priv->primary && priv->data && priv->pin_checked)
new_valid = TRUE;
mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid);
}
+
+static void initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data);
+
+static gboolean
+pin_check_again (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->pin_check_timeout = 0;
+ check_pin (self, initial_pin_check_done, NULL);
+ return FALSE;
+}
+
+static void
+initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMGenericGsmPrivate *priv;
+
+ /* modem could have been removed before we get here, in which case
+ * 'modem' will be NULL.
+ */
+ if (!modem)
+ return;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if ( error
+ && priv->pin_check_tries++ < 3
+ && !mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem))) {
+ /* Try it again a few times */
+ if (priv->pin_check_timeout)
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = g_timeout_add_seconds (2, pin_check_again, modem);
+ } else {
+ priv->pin_checked = TRUE;
+ mm_serial_port_close (MM_SERIAL_PORT (priv->primary));
+ check_valid (MM_GENERIC_GSM (modem));
+ }
+}
+
+static void
+initial_pin_check (MMGenericGsm *self)
+{
+ GError *error = NULL;
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error))
+ check_pin (self, initial_pin_check_done, NULL);
+ else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+
+ /* Ensure the modem is still somewhat usable if opening the serial
+ * port fails for some reason.
+ */
+ initial_pin_check_done (MM_MODEM (self), NULL, NULL);
+ }
+}
+
+static void
+initial_imei_check (MMGenericGsm *self)
+{
+ GError *error = NULL;
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->primary != NULL);
+
+ if (mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
+ /* Make sure echoing is off */
+ mm_at_serial_port_queue_command (priv->primary, "E0", 3, NULL, NULL);
+
+ /* Get modem's imei number */
+ mm_modem_gsm_card_get_imei (MM_MODEM_GSM_CARD (self),
+ get_imei_cb,
+ NULL);
+ } else {
+ g_warning ("%s: failed to open serial port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+}
+
static gboolean
owns_port (MMModem *modem, const char *subsys, const char *name)
{
@@ -308,28 +496,50 @@ mm_generic_gsm_grab_port (MMGenericGsm *self,
g_return_val_if_fail (!strcmp (subsys, "net") || !strcmp (subsys, "tty"), FALSE);
port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
- if (port && MM_IS_SERIAL_PORT (port)) {
- mm_serial_port_set_response_parser (MM_SERIAL_PORT (port),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ if (!port) {
+ g_warn_if_fail (port != NULL);
+ return NULL;
+ }
+
+ if (MM_IS_AT_SERIAL_PORT (port)) {
+ GPtrArray *array;
+ int i;
+
+ mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
- regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
- g_regex_unref (regex);
+ /* Set up CREG unsolicited message handlers */
+ array = mm_gsm_creg_regex_get (FALSE);
+ for (i = 0; i < array->len; i++) {
+ regex = g_ptr_array_index (array, i);
+
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
+ }
+ mm_gsm_creg_regex_destroy (array);
if (ptype == MM_PORT_TYPE_PRIMARY) {
- priv->primary = MM_SERIAL_PORT (port);
+ priv->primary = MM_AT_SERIAL_PORT (port);
if (!priv->data) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
}
- check_valid (self);
+
+ /* Get modem's initial lock/unlock state */
+ initial_pin_check (self);
+
+ /* Get modem's IMEI number */
+ initial_imei_check (self);
+
} else if (ptype == MM_PORT_TYPE_SECONDARY)
- priv->secondary = MM_SERIAL_PORT (port);
- } else {
+ priv->secondary = MM_AT_SERIAL_PORT (port);
+ } else if (MM_IS_QCDM_SERIAL_PORT (port)) {
+ if (!priv->qcdm)
+ priv->qcdm = MM_QCDM_SERIAL_PORT (port);
+ } else if (!strcmp (subsys, "net")) {
/* Net device (if any) is the preferred data port */
- if (!priv->data || MM_IS_SERIAL_PORT (priv->data)) {
+ if (!priv->data || MM_IS_AT_SERIAL_PORT (priv->data)) {
priv->data = port;
g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
check_valid (self);
@@ -381,7 +591,7 @@ release_port (MMModem *modem, const char *subsys, const char *name)
if (!port)
return;
- if (port == MM_PORT (priv->primary)) {
+ if (port == (MMPort *) priv->primary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->primary = NULL;
}
@@ -391,59 +601,375 @@ release_port (MMModem *modem, const char *subsys, const char *name)
g_object_notify (G_OBJECT (modem), MM_MODEM_DATA_DEVICE);
}
- if (port == MM_PORT (priv->secondary)) {
+ if (port == (MMPort *) priv->secondary) {
mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
priv->secondary = NULL;
}
+ if (port == (MMPort *) priv->qcdm) {
+ mm_modem_base_remove_port (MM_MODEM_BASE (modem), port);
+ priv->qcdm = NULL;
+ }
+
check_valid (MM_GENERIC_GSM (modem));
}
+static void
+reg_poll_response (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+
+ if (!error)
+ handle_reg_status_response (self, response, NULL);
+}
+
+static void
+periodic_signal_quality_cb (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+ /* Cached signal quality already updated */
+}
+
+static void
+periodic_access_tech_cb (MMModem *modem,
+ guint32 act,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem && !error && act)
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (modem), act);
+}
+
+static gboolean
+periodic_poll_cb (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMAtSerialPort *port;
+
+ port = mm_generic_gsm_get_best_at_port (self, NULL);
+ if (!port)
+ return TRUE; /* oh well, try later */
+
+ if (priv->creg_poll)
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, self);
+ if (priv->cgreg_poll)
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, self);
+
+ mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (self),
+ periodic_signal_quality_cb,
+ NULL);
+
+ if (MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology)
+ MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology (self, periodic_access_tech_cb, NULL);
+
+ return TRUE; /* continue running */
+}
+
+static void
+cgreg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ g_clear_error (&info->error);
+
+ /* The modem doesn't like unsolicited CGREG, so we'll need to poll */
+ priv->cgreg_poll = TRUE;
+ }
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+ }
+ mm_callback_info_schedule (info);
+}
+
+static void
+cgreg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Ignore errors except modem removal errors */
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ g_clear_error (&info->error);
+ /* Try CGREG=1 instead */
+ mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info);
+ } else {
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+
+ /* All done */
+ mm_callback_info_schedule (info);
+ }
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+creg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ if (info->error) {
+ g_clear_error (&info->error);
+
+ /* The modem doesn't like unsolicited CREG, so we'll need to poll */
+ priv->creg_poll = TRUE;
+ }
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+creg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Ignore errors except modem removal errors */
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ g_clear_error (&info->error);
+ mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info);
+ } else {
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ }
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+enable_failed (MMModem *modem, GError *error, MMCallbackInfo *info)
+{
+ MMGenericGsmPrivate *priv;
+
+ info->error = mm_modem_check_removed (modem, error);
+
+ if (modem) {
+ mm_modem_set_state (modem,
+ MM_MODEM_STATE_DISABLED,
+ MM_MODEM_STATE_REASON_NONE);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (priv->primary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->primary)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->primary));
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary));
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static guint32 best_charsets[] = {
+ MM_MODEM_CHARSET_UTF8,
+ MM_MODEM_CHARSET_UCS2,
+ MM_MODEM_CHARSET_8859_1,
+ MM_MODEM_CHARSET_IRA,
+ MM_MODEM_CHARSET_UNKNOWN
+};
+
+static void
+enabled_set_charset_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ guint idx;
+
+ /* only modem removals are really a hard error */
+ if (error) {
+ if (!modem) {
+ enable_failed (modem, error, info);
+ return;
+ }
+
+ /* Try the next best charset */
+ idx = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "best-charset")) + 1;
+ if (best_charsets[idx] == MM_MODEM_CHARSET_UNKNOWN) {
+ GError *tmp_error;
+
+ /* No more character sets we can use */
+ tmp_error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Failed to find a usable modem character set");
+ enable_failed (modem, tmp_error, info);
+ g_error_free (tmp_error);
+ } else {
+ /* Send the new charset */
+ mm_callback_info_set_data (info, "best-charset", GUINT_TO_POINTER (idx), NULL);
+ mm_modem_set_charset (modem, best_charsets[idx], enabled_set_charset_done, info);
+ }
+ } else {
+ /* Modem is now enabled; update the state */
+ mm_generic_gsm_update_enabled_state (MM_GENERIC_GSM (modem), FALSE, MM_MODEM_STATE_REASON_NONE);
+
+ /* Set up unsolicited registration notifications */
+ mm_at_serial_port_queue_command (MM_GENERIC_GSM_GET_PRIVATE (modem)->primary,
+ "+CREG=2", 3, creg2_done, info);
+ }
+}
+
+static void
+supported_charsets_done (MMModem *modem,
+ guint32 charsets,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (!modem) {
+ enable_failed (modem, error, info);
+ return;
+ }
+
+ /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
+ mm_modem_set_charset (modem, MM_MODEM_CHARSET_UTF8, enabled_set_charset_done, info);
+}
+
+static void
+get_allowed_mode_done (MMModem *modem,
+ MMModemGsmAllowedMode mode,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (modem),
+ error ? MM_MODEM_GSM_ALLOWED_MODE_ANY : mode);
+ }
+}
+
+static void
+get_enable_info_done (MMModem *modem,
+ const char *manufacturer,
+ const char *model,
+ const char *version,
+ GError *error,
+ gpointer user_data)
+{
+ /* Modem base class handles the response for us */
+}
+
void
-mm_generic_gsm_enable_complete (MMGenericGsm *modem,
+mm_generic_gsm_enable_complete (MMGenericGsm *self,
GError *error,
MMCallbackInfo *info)
{
- g_return_if_fail (modem != NULL);
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
g_return_if_fail (info != NULL);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
if (error) {
- mm_modem_set_state (MM_MODEM (modem),
- MM_MODEM_STATE_DISABLED,
- MM_MODEM_STATE_REASON_NONE);
+ enable_failed ((MMModem *) self, error, info);
+ return;
+ }
- info->error = g_error_copy (error);
- } else {
- /* Modem is enabled; update the state */
- mm_generic_gsm_update_enabled_state (modem, FALSE, MM_MODEM_STATE_REASON_NONE);
+ /* Open the second port here if the modem has one. We'll use it for
+ * signal strength and registration updates when the device is connected,
+ * but also many devices will send unsolicited registration or other
+ * messages to the secondary port but not the primary.
+ */
+ if (priv->secondary) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &error)) {
+ if (mm_options_debug ()) {
+ g_warning ("%s: error opening secondary port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ }
+ }
}
- mm_callback_info_schedule (info);
+ /* Try to enable XON/XOFF flow control */
+ mm_at_serial_port_queue_command (priv->primary, "+IFC=1,1", 3, NULL, NULL);
+
+ /* Grab device info right away */
+ mm_modem_get_info (MM_MODEM (self), get_enable_info_done, NULL);
+
+ /* Get allowed mode */
+ if (MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode)
+ MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode (self, get_allowed_mode_done, NULL);
+
+ /* And supported character sets */
+ mm_modem_get_supported_charsets (MM_MODEM (self), supported_charsets_done, info);
}
static void
-enable_done (MMSerialPort *port,
+real_do_enable_power_up_done (MMGenericGsm *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ /* Ignore power-up errors as not all devices actually support CFUN=1 */
+ mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
+}
+
+static void
+enable_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- /* Ignore power-up command errors, not all devices actually support
- * CFUN=1.
- */
- /* FIXME: instead of just ignoring errors, since we allow subclasses
- * to override the power-on command, make a class function for powering
- * on the phone and let the subclass decided whether it wants to handle
- * errors or ignore them.
+ /* Let subclasses handle the power up command response/error; many devices
+ * don't support +CFUN, but for those that do let them handle the error
+ * correctly.
*/
-
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
+ g_assert (MM_GENERIC_GSM_GET_CLASS (info->modem)->do_enable_power_up_done);
+ MM_GENERIC_GSM_GET_CLASS (info->modem)->do_enable_power_up_done (MM_GENERIC_GSM (info->modem),
+ response,
+ error,
+ info);
}
static void
-init_done (MMSerialPort *port,
+init_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -457,22 +983,22 @@ init_done (MMSerialPort *port,
}
/* Ensure echo is off after the init command; some modems ignore the
- * E0 when it's in the same like as ATZ (Option GIO322).
- */
- mm_serial_port_queue_command (port, "E0 +CMEE=1", 2, NULL, NULL);
+ * E0 when it's in the same line as ATZ (Option GIO322).
+ */
+ mm_at_serial_port_queue_command (port, "E0", 2, NULL, NULL);
+
+ /* Some phones (like Blackberries) don't support +CMEE=1, so make it
+ * optional. It completely violates 3GPP TS 27.007 (9.1) but what can we do...
+ */
+ mm_at_serial_port_queue_command (port, "+CMEE=1", 2, NULL, NULL);
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD_OPTIONAL, &cmd, NULL);
- mm_serial_port_queue_command (port, cmd, 2, NULL, NULL);
+ mm_at_serial_port_queue_command (port, cmd, 2, NULL, NULL);
g_free (cmd);
- if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->unsolicited_registration)
- mm_serial_port_queue_command (port, "+CREG=1", 5, NULL, NULL);
- else
- mm_serial_port_queue_command (port, "+CREG=0", 5, NULL, NULL);
-
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_UP_CMD, &cmd, NULL);
if (cmd && strlen (cmd))
- mm_serial_port_queue_command (port, cmd, 5, enable_done, user_data);
+ mm_at_serial_port_queue_command (port, cmd, 5, enable_done, user_data);
else
enable_done (port, NULL, NULL, user_data);
g_free (cmd);
@@ -490,7 +1016,7 @@ enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data)
}
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_INIT_CMD, &cmd, NULL);
- mm_serial_port_queue_command (port, cmd, 3, init_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 3, init_done, user_data);
g_free (cmd);
}
@@ -501,7 +1027,7 @@ real_do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data)
MMCallbackInfo *info;
info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
- mm_serial_port_flash (priv->primary, 100, enable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 100, FALSE, enable_flash_done, info);
}
static void
@@ -511,11 +1037,25 @@ enable (MMModem *modem,
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
GError *error = NULL;
+ const char *unlock;
+
+ /* If the device needs a PIN, deal with that now, but we don't care
+ * about SIM-PIN2/SIM-PUK2 since the device is operational without it.
+ */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock && strcmp (unlock, "sim-puk2") && strcmp (unlock, "sim-pin2")) {
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+ info->error = error_for_unlock_required (unlock);
+ mm_callback_info_schedule (info);
+ return;
+ }
/* First, reset the previously used CID */
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
+ priv->cid = -1;
- if (!mm_serial_port_open (priv->primary, &error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->primary), &error)) {
MMCallbackInfo *info;
g_assert (error);
@@ -532,7 +1072,7 @@ enable (MMModem *modem,
}
static void
-disable_done (MMSerialPort *port,
+disable_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -541,13 +1081,25 @@ disable_done (MMSerialPort *port,
info->error = mm_modem_check_removed (info->modem, error);
if (!info->error) {
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
- mm_serial_port_close (port);
+ mm_serial_port_close_force (MM_SERIAL_PORT (port));
mm_modem_set_state (MM_MODEM (info->modem),
MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_REASON_NONE);
- priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
+
+ /* Clear out circuit-switched registration info... */
+ reg_info_updated (self,
+ MM_GENERIC_GSM_REG_TYPE_CS,
+ TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN,
+ TRUE, NULL,
+ TRUE, NULL);
+ /* ...and packet-switched registration info */
+ reg_info_updated (self,
+ MM_GENERIC_GSM_REG_TYPE_PS,
+ TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN,
+ TRUE, NULL,
+ TRUE, NULL);
}
mm_callback_info_schedule (info);
}
@@ -575,11 +1127,15 @@ disable_flash_done (MMSerialPort *port,
return;
}
+ /* Disable unsolicited messages */
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT+CREG=0", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "AT+CGREG=0", 3, NULL, NULL);
+
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_DOWN_CMD, &cmd, NULL);
if (cmd && strlen (cmd))
- mm_serial_port_queue_command (port, cmd, 5, disable_done, user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), cmd, 5, disable_done, user_data);
else
- disable_done (port, NULL, NULL, user_data);
+ disable_done (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data);
g_free (cmd);
}
@@ -588,14 +1144,42 @@ disable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
MMModemState state;
/* First, reset the previously used CID and clean up registration */
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
+ g_warn_if_fail (priv->cid == -1);
+ priv->cid = -1;
+
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ if (priv->signal_quality_id) {
+ g_source_remove (priv->signal_quality_id);
+ priv->signal_quality_id = 0;
+ }
+
+ if (priv->pin_check_timeout) {
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
+
+ priv->lac[0] = 0;
+ priv->lac[1] = 0;
+ priv->cell_id[0] = 0;
+ priv->cell_id[1] = 0;
+ _internal_update_access_technology (self, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+
+ /* Close the secondary port if its open */
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (priv->secondary));
+
info = mm_callback_info_new (modem, callback, user_data);
/* Cache the previous state so we can reset it if the operation fails */
@@ -610,13 +1194,13 @@ disable (MMModem *modem,
MM_MODEM_STATE_REASON_NONE);
if (mm_port_get_connected (MM_PORT (priv->primary)))
- mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disable_flash_done, info);
else
- disable_flash_done (priv->primary, NULL, info);
+ disable_flash_done (MM_SERIAL_PORT (priv->primary), NULL, info);
}
static void
-get_string_done (MMSerialPort *port,
+get_string_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -632,6 +1216,110 @@ get_string_done (MMSerialPort *port,
}
static void
+get_mnc_length_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ int sw1, sw2;
+ const char *imsi;
+ gboolean success = FALSE;
+ char hex[51];
+ char *bin;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ memset (hex, 0, sizeof (hex));
+ if (sscanf (response->str, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ else {
+ /* May not include quotes... */
+ if (sscanf (response->str, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
+ success = TRUE;
+ }
+
+ if (!success) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse the CRSM response");
+ goto done;
+ }
+
+ if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) {
+ gsize buflen = 0;
+ guint32 mnc_len;
+
+ /* Make sure the buffer is only hex characters */
+ while (buflen < sizeof (hex) && hex[buflen]) {
+ if (!isxdigit (hex[buflen])) {
+ hex[buflen] = 0x0;
+ break;
+ }
+ buflen++;
+ }
+
+ /* Convert hex string to binary */
+ bin = utils_hexstr2bin (hex, &buflen);
+ if (!bin || buflen < 4) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned malformed response '%s'",
+ hex);
+ goto done;
+ }
+
+ /* MNC length is byte 4 of this SIM file */
+ mnc_len = bin[3] & 0xFF;
+ if (mnc_len == 2 || mnc_len == 3) {
+ imsi = mm_callback_info_get_data (info, "imsi");
+ mm_callback_info_set_result (info, g_strndup (imsi, 3 + mnc_len), g_free);
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)",
+ mnc_len);
+ }
+ } else {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ }
+
+done:
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_operator_id_imsi_done (MMModem *modem,
+ const char *result,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_callback_info_set_data (info, "imsi", g_strdup (result), g_free);
+
+ /* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
+ mm_at_serial_port_queue_command_cached (priv->primary,
+ "+CRSM=176,28589,0,0,4",
+ 3,
+ get_mnc_length_done,
+ info);
+}
+
+static void
get_imei (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
@@ -640,7 +1328,7 @@ get_imei (MMModemGsmCard *modem,
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
+ mm_at_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
}
static void
@@ -652,112 +1340,116 @@ get_imsi (MMModemGsmCard *modem,
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
+ mm_at_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
}
static void
-card_info_invoke (MMCallbackInfo *info)
+get_operator_id (MMModemGsmCard *modem,
+ MMModemStringFn callback,
+ gpointer user_data)
{
- MMModemInfoFn callback = (MMModemInfoFn) info->callback;
-
- callback (info->modem,
- (char *) mm_callback_info_get_data (info, "card-info-manufacturer"),
- (char *) mm_callback_info_get_data (info, "card-info-model"),
- (char *) mm_callback_info_get_data (info, "card-info-version"),
- info->error, info->user_data);
-}
-
-#define GMI_RESP_TAG "+CGMI:"
-#define GMM_RESP_TAG "+CGMM:"
-#define GMR_RESP_TAG "+CGMR:"
+ MMCallbackInfo *info;
-static const char *
-strip_tag (const char *str, const char *tag)
-{
- /* Strip the response header, if any */
- if (strncmp (str, tag, strlen (tag)) == 0)
- str += strlen (tag);
- while (*str && isspace (*str))
- str++;
- return str;
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+ mm_modem_gsm_card_get_imsi (MM_MODEM_GSM_CARD (modem),
+ get_operator_id_imsi_done,
+ info);
}
static void
-get_version_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+get_card_info (MMModem *modem,
+ MMModemInfoFn callback,
+ gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *resp = strip_tag (response->str, GMR_RESP_TAG);
-
- if (!error)
- mm_callback_info_set_data (info, "card-info-version", g_strdup (resp), g_free);
- else if (!info->error)
- info->error = g_error_copy (error);
+ MMAtSerialPort *port;
+ GError *error = NULL;
- mm_callback_info_schedule (info);
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &error);
+ mm_modem_base_get_card_info (MM_MODEM_BASE (modem), port, error, callback, user_data);
+ g_clear_error (&error);
}
+#define PIN_PORT_TAG "pin-port"
+
static void
-get_model_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data);
+
+static gboolean
+pin_puk_recheck_again (gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *resp = strip_tag (response->str, GMM_RESP_TAG);
- if (!error)
- mm_callback_info_set_data (info, "card-info-model", g_strdup (resp), g_free);
- else if (!info->error)
- info->error = g_error_copy (error);
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pin_check_timeout = 0;
+ check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
+ return FALSE;
}
static void
-get_manufacturer_done (MMSerialPort *port,
- GString *response,
- GError *error,
- gpointer user_data)
+pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *resp = strip_tag (response->str, GMI_RESP_TAG);
+ MMSerialPort *port;
- if (!error)
- mm_callback_info_set_data (info, "card-info-manufacturer", g_strdup (resp), g_free);
- else
- info->error = g_error_copy (error);
-}
+ /* Clear the pin check timeout to ensure that it won't ever get a
+ * stale MMCallbackInfo if the modem got removed. We'll reschedule it here
+ * anyway if needed.
+ */
+ if (info->modem) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
-static void
-get_card_info (MMModem *modem,
- MMModemInfoFn callback,
- gpointer user_data)
-{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
- MMCallbackInfo *info;
+ if (priv->pin_check_timeout)
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
- info = mm_callback_info_new_full (MM_MODEM (modem),
- card_info_invoke,
- G_CALLBACK (callback),
- user_data);
+ /* modem could have been removed before we get here, in which case
+ * 'modem' will be NULL.
+ */
+ info->error = mm_modem_check_removed (modem, error);
+
+ /* If the modem wasn't removed, and the modem isn't ready yet, ask it for
+ * the current PIN status a few times since some devices take a bit to fully
+ * enable themselves after a SIM PIN/PUK unlock.
+ */
+ if ( info->modem
+ && info->error
+ && !g_error_matches (info->error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- mm_serial_port_queue_command_cached (priv->primary, "+CGMI", 3, get_manufacturer_done, info);
- mm_serial_port_queue_command_cached (priv->primary, "+CGMM", 3, get_model_done, info);
- mm_serial_port_queue_command_cached (priv->primary, "+CGMR", 3, get_version_done, info);
+ if (priv->pin_check_tries < 4) {
+ g_clear_error (&info->error);
+ priv->pin_check_tries++;
+ priv->pin_check_timeout = g_timeout_add_seconds (2, pin_puk_recheck_again, info);
+ return;
+ }
+ }
+
+ /* Otherwise, clean up and return the PIN check result */
+ port = mm_callback_info_get_data (info, PIN_PORT_TAG);
+ if (modem && port)
+ mm_serial_port_close (port);
+
+ mm_callback_info_schedule (info);
}
static void
-send_puk_done (MMSerialPort *port,
+send_puk_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error)
+ if (error) {
info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
+ mm_callback_info_schedule (info);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ }
+
+ /* Get latest PIN status */
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pin_check_tries = 0;
+ check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
}
static void
@@ -767,27 +1459,52 @@ send_puk (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
+ MMAtSerialPort *port;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ /* Ensure we have a usable port to use for the unlock */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ mm_callback_info_set_data (info, PIN_PORT_TAG, port, NULL);
+
command = g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin);
- mm_serial_port_queue_command (priv->primary, command, 3, send_puk_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, send_puk_done, info);
g_free (command);
}
static void
-send_pin_done (MMSerialPort *port,
+send_pin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error)
+ if (error) {
info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
+ mm_callback_info_schedule (info);
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ }
+
+ /* Get latest PIN status */
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pin_check_tries = 0;
+ check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
}
static void
@@ -796,18 +1513,36 @@ send_pin (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
+ MMAtSerialPort *port;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ /* Ensure we have a usable port to use for the unlock */
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Modem may not be enabled yet, which sometimes can't be done until
+ * the device has been unlocked. In this case we have to open the port
+ * ourselves.
+ */
+ if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ mm_callback_info_set_data (info, PIN_PORT_TAG, port, NULL);
+
command = g_strdup_printf ("+CPIN=\"%s\"", pin);
- mm_serial_port_queue_command (priv->primary, command, 3, send_pin_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, send_pin_done, info);
g_free (command);
}
static void
-enable_pin_done (MMSerialPort *port,
+enable_pin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -832,12 +1567,12 @@ enable_pin (MMModemGsmCard *modem,
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin);
- mm_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info);
g_free (command);
}
static void
-change_pin_done (MMSerialPort *port,
+change_pin_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -862,12 +1597,104 @@ change_pin (MMModemGsmCard *modem,
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin);
- mm_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info);
g_free (command);
}
+static void
+get_unlock_retries (MMModemGsmCard *modem,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ mm_callback_info_set_result (info,
+ GUINT_TO_POINTER (MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED),
+ NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_info_updated (MMGenericGsm *self,
+ gboolean update_rs,
+ MMGenericGsmRegType rs_type,
+ MMModemGsmNetworkRegStatus status,
+ gboolean update_code,
+ const char *oper_code,
+ gboolean update_name,
+ const char *oper_name)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMModemGsmNetworkRegStatus old_status;
+ gboolean changed = FALSE;
+
+ if (update_rs) {
+ g_return_if_fail ( rs_type == MM_GENERIC_GSM_REG_TYPE_CS
+ || rs_type == MM_GENERIC_GSM_REG_TYPE_PS);
+
+ old_status = gsm_reg_status (self);
+ priv->reg_status[rs_type - 1] = status;
+ if (gsm_reg_status (self) != old_status)
+ changed = TRUE;
+ }
+
+ if (update_code) {
+ if (g_strcmp0 (oper_code, priv->oper_code) != 0) {
+ g_free (priv->oper_code);
+ priv->oper_code = g_strdup (oper_code);
+ changed = TRUE;
+ }
+ }
+
+ if (update_name) {
+ if (g_strcmp0 (oper_name, priv->oper_name) != 0) {
+ g_free (priv->oper_name);
+ priv->oper_name = g_strdup (oper_name);
+ changed = TRUE;
+ }
+ }
+
+ if (changed) {
+ mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (self),
+ gsm_reg_status (self),
+ priv->oper_code,
+ priv->oper_name);
+ }
+}
+
+static void
+convert_operator_from_ucs2 (char **operator)
+{
+ const char *p;
+ char *converted;
+ size_t len;
+
+ g_return_if_fail (operator != NULL);
+ g_return_if_fail (*operator != NULL);
+
+ p = *operator;
+ len = strlen (p);
+
+ /* Len needs to be a multiple of 4 for UCS2 */
+ if ((len < 4) || ((len % 4) != 0))
+ return;
+
+ while (*p) {
+ if (!isxdigit (*p++))
+ return;
+ }
+
+ converted = mm_modem_charset_hex_to_utf8 (*operator, MM_MODEM_CHARSET_UCS2);
+ if (converted) {
+ g_free (*operator);
+ *operator = converted;
+ }
+}
+
static char *
-parse_operator (const char *reply)
+parse_operator (const char *reply, MMModemCharset cur_charset)
{
char *operator = NULL;
@@ -889,52 +1716,53 @@ parse_operator (const char *reply)
g_regex_unref (r);
}
+ /* Some modems (Option & HSO) return the operator name as a hexadecimal
+ * string of the bytes of the operator name as encoded by the current
+ * character set.
+ */
+ if (operator && (cur_charset == MM_MODEM_CHARSET_UCS2))
+ convert_operator_from_ucs2 (&operator);
+
return operator;
}
static void
-read_operator_code_done (MMSerialPort *port,
+read_operator_code_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data);
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
char *oper;
- if (error)
- return;
-
- oper = parse_operator (response->str);
- if (!oper)
- return;
-
- g_free (priv->oper_code);
- priv->oper_code = oper;
+ if (!error) {
+ oper = parse_operator (response->str, MM_MODEM_CHARSET_UNKNOWN);
+ if (oper) {
+ reg_info_updated (self, FALSE, MM_GENERIC_GSM_REG_TYPE_UNKNOWN, 0,
+ TRUE, oper,
+ FALSE, NULL);
+ }
+ }
}
static void
-read_operator_name_done (MMSerialPort *port,
+read_operator_name_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data);
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
char *oper;
- if (error)
- return;
-
- oper = parse_operator (response->str);
- if (!oper)
- return;
-
- g_free (priv->oper_name);
- priv->oper_name = oper;
-
- mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (user_data),
- priv->reg_status,
- priv->oper_code,
- priv->oper_name);
+ if (!error) {
+ oper = parse_operator (response->str, priv->cur_charset);
+ if (oper) {
+ reg_info_updated (self, FALSE, MM_GENERIC_GSM_REG_TYPE_UNKNOWN, 0,
+ FALSE, NULL,
+ TRUE, oper);
+ }
+ }
}
/* Registration */
@@ -961,8 +1789,93 @@ mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem)
}
}
+static void
+got_signal_quality (MMModem *modem,
+ guint32 quality,
+ GError *error,
+ gpointer user_data)
+{
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (modem), quality);
+}
+
+static void
+roam_disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ g_message ("Disconnected because roaming is not allowed");
+}
+
+static void
+get_reg_act_done (MMModem *modem,
+ guint32 act,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem && !error && act)
+ mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (modem), act);
+}
+
+void
+mm_generic_gsm_set_reg_status (MMGenericGsm *self,
+ MMGenericGsmRegType rs_type,
+ MMModemGsmNetworkRegStatus status)
+{
+ MMGenericGsmPrivate *priv;
+ MMAtSerialPort *port;
+
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ g_return_if_fail ( rs_type == MM_GENERIC_GSM_REG_TYPE_CS
+ || rs_type == MM_GENERIC_GSM_REG_TYPE_PS);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (priv->reg_status[rs_type - 1] == status)
+ return;
+
+ g_debug ("%s registration state changed: %d",
+ (rs_type == MM_GENERIC_GSM_REG_TYPE_CS) ? "CS" : "PS",
+ status);
+ priv->reg_status[rs_type - 1] = status;
+
+ port = mm_generic_gsm_get_best_at_port (self, NULL);
+
+ if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
+ status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
+
+ /* If we're connected and we're not supposed to roam, but the device
+ * just roamed, disconnect the connection to avoid charging the user
+ * loads of money.
+ */
+ if ( (status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)
+ && (mm_modem_get_state (MM_MODEM (self)) == MM_MODEM_STATE_CONNECTED)
+ && (priv->roam_allowed == FALSE)) {
+ mm_modem_disconnect (MM_MODEM (self), roam_disconnect_done, NULL);
+ } else {
+ /* Grab the new operator name and MCC/MNC */
+ if (port) {
+ mm_at_serial_port_queue_command (port, "+COPS=3,2;+COPS?", 3, read_operator_code_done, self);
+ mm_at_serial_port_queue_command (port, "+COPS=3,0;+COPS?", 3, read_operator_name_done, self);
+ }
+
+ /* And update signal quality and access technology */
+ mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (self), got_signal_quality, NULL);
+ if (MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology)
+ MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology (self, get_reg_act_done, NULL);
+ }
+ } else
+ reg_info_updated (self, FALSE, rs_type, 0, TRUE, NULL, TRUE, NULL);
+
+ mm_generic_gsm_update_enabled_state (self, TRUE, MM_MODEM_STATE_REASON_NONE);
+}
+
+/* Returns TRUE if the modem is "done", ie has registered or been denied */
static gboolean
-reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
+reg_status_updated (MMGenericGsm *self,
+ MMGenericGsmRegType rs_type,
+ int new_value,
+ GError **error)
{
MMModemGsmNetworkRegStatus status;
gboolean status_done = FALSE;
@@ -991,7 +1904,7 @@ reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
break;
}
- mm_generic_gsm_set_reg_status (self, status);
+ mm_generic_gsm_set_reg_status (self, rs_type, status);
/* Registration has either completed successfully or completely failed */
switch (status) {
@@ -1022,21 +1935,35 @@ reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
return status_done;
}
+static MMGenericGsmRegType
+cgreg_to_reg_type (gboolean cgreg)
+{
+ return (cgreg ? MM_GENERIC_GSM_REG_TYPE_PS : MM_GENERIC_GSM_REG_TYPE_CS);
+}
+
static void
-reg_state_changed (MMSerialPort *port,
+reg_state_changed (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
MMGenericGsm *self = MM_GENERIC_GSM (user_data);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- char *str;
- gboolean done;
+ guint32 state = 0, idx;
+ gulong lac = 0, cell_id = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ GError *error = NULL;
- str = g_match_info_fetch (match_info, 1);
- done = reg_status_updated (self, atoi (str), NULL);
- g_free (str);
+ if (!mm_gsm_parse_creg_response (match_info, &state, &lac, &cell_id, &act, &cgreg, &error)) {
+ if (mm_options_debug ()) {
+ g_warning ("%s: error parsing unsolicited registration: %s",
+ __func__,
+ error && error->message ? error->message : "(unknown)");
+ }
+ return;
+ }
- if (done) {
+ if (reg_status_updated (self, cgreg_to_reg_type (cgreg), state, NULL)) {
/* If registration is finished (either registered or failed) but the
* registration query hasn't completed yet, just remove the timeout and
* let the registration query complete.
@@ -1046,6 +1973,14 @@ reg_state_changed (MMSerialPort *port,
priv->pending_reg_id = 0;
}
}
+
+ idx = cgreg ? 1 : 0;
+ priv->lac[idx] = lac;
+ priv->cell_id[idx] = cell_id;
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act));
}
static gboolean
@@ -1072,8 +2007,62 @@ reg_status_again_remove (gpointer data)
g_source_remove (id);
}
+static gboolean
+handle_reg_status_response (MMGenericGsm *self,
+ GString *response,
+ GError **error)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ GMatchInfo *match_info;
+ guint32 status = 0, idx;
+ gulong lac = 0, ci = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ guint i;
+
+ /* Try to match the response */
+ for (i = 0; i < priv->reg_regex->len; i++) {
+ GRegex *r = g_ptr_array_index (priv->reg_regex, i);
+
+ if (g_regex_match (r, response->str, 0, &match_info))
+ break;
+ g_match_info_free (match_info);
+ match_info = NULL;
+ }
+
+ if (!match_info) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Unknown registration status response");
+ return FALSE;
+ }
+
+ /* And parse it */
+ if (!mm_gsm_parse_creg_response (match_info, &status, &lac, &ci, &act, &cgreg, error)) {
+ g_match_info_free (match_info);
+ return FALSE;
+ }
+
+ /* Success; update cached location information */
+ idx = cgreg ? 1 : 0;
+ priv->lac[idx] = lac;
+ priv->cell_id[idx] = ci;
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act));
+
+ if (status >= 0) {
+ /* Update cached registration status */
+ reg_status_updated (self, cgreg_to_reg_type (cgreg), status, NULL);
+ }
+
+ return TRUE;
+}
+
+#define CGREG_TRIED_TAG "cgreg-tried"
+
static void
-get_reg_status_done (MMSerialPort *port,
+get_reg_status_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1081,76 +2070,71 @@ get_reg_status_done (MMSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- int reg_status = -1;
- GRegex *r;
- GMatchInfo *match_info;
- char *tmp;
guint id;
+ MMModemGsmNetworkRegStatus status;
- g_warn_if_fail (info == priv->pending_reg_info);
+ /* This function should only get called during the connect sequence when
+ * polling for registration state, since explicit registration requests
+ * from D-Bus clients are filled from the cached registration state.
+ */
+ g_return_if_fail (info == priv->pending_reg_info);
if (error) {
- info->error = g_error_copy (error);
- goto reg_done;
- }
-
- r = g_regex_new ("\\+CREG:\\s*(\\d+),\\s*(\\d+)",
- G_REGEX_RAW | G_REGEX_OPTIMIZE,
- 0, &info->error);
- if (r) {
- g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error);
- if (g_match_info_matches (match_info)) {
- /* Get reg status */
- tmp = g_match_info_fetch (match_info, 2);
- if (isdigit (tmp[0])) {
- tmp[1] = '\0';
- reg_status = atoi (tmp);
- } else {
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Unknown registration status '%s'",
- tmp);
- }
- g_free (tmp);
+ gboolean cgreg_tried = !!mm_callback_info_get_data (info, CGREG_TRIED_TAG);
+
+ /* If this was a +CREG error, try +CGREG. Some devices (blackberries)
+ * respond to +CREG with an error but return a valid +CGREG response.
+ * So try both. If we get an error from both +CREG and +CGREG, that's
+ * obviously a hard fail.
+ */
+ if (cgreg_tried == FALSE) {
+ mm_callback_info_set_data (info, CGREG_TRIED_TAG, GUINT_TO_POINTER (TRUE), NULL);
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, get_reg_status_done, info);
+ return;
} else {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse the registration status response");
+ info->error = g_error_copy (error);
+ goto reg_done;
}
- g_match_info_free (match_info);
- g_regex_unref (r);
}
- if ( reg_status >= 0
- && !reg_status_updated (self, reg_status, &info->error)
- && priv->pending_reg_info) {
- g_clear_error (&info->error);
+ /* The unsolicited registration state handlers will intercept the CREG
+ * response and update the cached registration state for us, so we usually
+ * just need to check the cached state here.
+ */
+
+ if (strlen (response->str)) {
+ /* But just in case the unsolicited handlers doesn't do it... */
+ if (!handle_reg_status_response (self, response, &info->error))
+ goto reg_done;
+ }
- /* Not registered yet; poll registration status again */
+ status = gsm_reg_status (self);
+ if ( status != MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ && status != MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING
+ && status != MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED) {
+ /* If we're still waiting for automatic registration to complete or
+ * fail, check again in a few seconds.
+ */
id = g_timeout_add_seconds (1, reg_status_again, info);
mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG,
- GUINT_TO_POINTER (id),
- reg_status_again_remove);
+ GUINT_TO_POINTER (id),
+ reg_status_again_remove);
return;
}
reg_done:
- /* This will schedule the callback for us */
+ /* This will schedule the pending registration's the callback for us */
mm_generic_gsm_pending_registration_stop (self);
}
static void
-get_registration_status (MMSerialPort *port, MMCallbackInfo *info)
+get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
-
- g_warn_if_fail (info == priv->pending_reg_info);
-
- mm_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info);
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info);
}
static void
-register_done (MMSerialPort *port,
+register_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1169,7 +2153,9 @@ register_done (MMSerialPort *port,
if (priv->pending_reg_info) {
g_warn_if_fail (info == priv->pending_reg_info);
- /* Ignore errors here, get the actual registration status */
+ /* Don't use cached registration state here since it could be up to
+ * 30 seconds old. Get fresh registration state.
+ */
get_registration_status (port, info);
}
}
@@ -1182,7 +2168,16 @@ registration_timed_out (gpointer data)
g_warn_if_fail (info == priv->pending_reg_info);
- priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
+ /* Clear out circuit-switched registration info... */
+ reg_info_updated (MM_GENERIC_GSM (info->modem),
+ TRUE, MM_GENERIC_GSM_REG_TYPE_CS, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE,
+ TRUE, NULL,
+ TRUE, NULL);
+ /* ... and packet-switched registration info */
+ reg_info_updated (MM_GENERIC_GSM (info->modem),
+ TRUE, MM_GENERIC_GSM_REG_TYPE_PS, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE,
+ TRUE, NULL,
+ TRUE, NULL);
info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (info->modem));
@@ -1190,28 +2185,47 @@ registration_timed_out (gpointer data)
return FALSE;
}
+static gboolean
+reg_is_idle (MMModemGsmNetworkRegStatus status)
+{
+ if ( status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ || status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING
+ || status == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING)
+ return FALSE;
+ return TRUE;
+}
+
static void
do_register (MMModemGsmNetwork *modem,
const char *network_id,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
- char *command;
+ char *command = NULL;
/* Clear any previous registration */
- mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ mm_generic_gsm_pending_registration_stop (self);
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
priv->pending_reg_id = g_timeout_add_seconds (60, registration_timed_out, info);
priv->pending_reg_info = info;
- if (network_id)
+ /* If the user sent a specific network to use, lock it in. If no specific
+ * network was given, and the modem is not registered and not searching,
+ * kick it to search for a network. Also do auto registration if the modem
+ * had been set to manual registration last time but now is not.
+ */
+ if (network_id) {
command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id);
- else
+ priv->manual_reg = TRUE;
+ } else if (reg_is_idle (gsm_reg_status (self)) || priv->manual_reg) {
command = g_strdup ("+COPS=0,,");
+ priv->manual_reg = FALSE;
+ }
/* Ref the callback info to ensure it stays alive for register_done() even
* if the timeout triggers and ends registration (which calls the callback
@@ -1231,8 +2245,12 @@ do_register (MMModemGsmNetwork *modem,
* the +COPS response is never received.
*/
mm_callback_info_ref (info);
- mm_serial_port_queue_command (priv->primary, command, 120, register_done, info);
- g_free (command);
+
+ if (command) {
+ mm_at_serial_port_queue_command (priv->primary, command, 120, register_done, info);
+ g_free (command);
+ } else
+ register_done (priv->primary, NULL, NULL, info);
}
static void
@@ -1242,7 +2260,7 @@ gsm_network_reg_info_invoke (MMCallbackInfo *info)
MMModemGsmNetworkRegInfoFn callback = (MMModemGsmNetworkRegInfoFn) info->callback;
callback (MM_MODEM_GSM_NETWORK (info->modem),
- priv->reg_status,
+ gsm_reg_status (MM_GENERIC_GSM (info->modem)),
priv->oper_code,
priv->oper_name,
info->error,
@@ -1260,7 +2278,9 @@ get_registration_info (MMModemGsmNetwork *self,
gsm_network_reg_info_invoke,
G_CALLBACK (callback),
user_data);
-
+ /* Registration info updates are handled internally either by unsolicited
+ * updates or by polling. Thus just return the cached registration state.
+ */
mm_callback_info_schedule (info);
}
@@ -1292,7 +2312,7 @@ mm_generic_gsm_connect_complete (MMGenericGsm *modem,
}
static void
-connect_report_done (MMSerialPort *port,
+connect_report_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1323,7 +2343,7 @@ connect_report_done (MMSerialPort *port,
}
static void
-connect_done (MMSerialPort *port,
+connect_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1335,7 +2355,7 @@ connect_done (MMSerialPort *port,
info->error = g_error_copy (error);
/* Try to get more information why it failed */
priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- mm_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info);
+ mm_at_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info);
} else
mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), NULL, info);
}
@@ -1369,21 +2389,21 @@ connect (MMModem *modem,
} else
command = g_strconcat ("DT", number, NULL);
- mm_serial_port_queue_command (priv->primary, command, 60, connect_done, info);
+ mm_at_serial_port_queue_command (priv->primary, command, 60, connect_done, info);
g_free (command);
}
static void
-disconnect_flash_done (MMSerialPort *port,
- GError *error,
- gpointer user_data)
+disconnect_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMModemState prev_state;
- info->error = mm_modem_check_removed (info->modem, error);
+ info->error = mm_modem_check_removed (modem, error);
if (info->error) {
- if (info->modem) {
+ if (info->modem && modem) {
/* Reset old state since the operation failed */
prev_state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, MM_GENERIC_GSM_PREV_STATE_TAG));
mm_modem_set_state (MM_MODEM (info->modem),
@@ -1391,10 +2411,11 @@ disconnect_flash_done (MMSerialPort *port,
MM_MODEM_STATE_REASON_NONE);
}
} else {
- MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
mm_port_set_connected (priv->data, FALSE);
+ priv->cid = -1;
mm_generic_gsm_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
}
@@ -1402,16 +2423,138 @@ disconnect_flash_done (MMSerialPort *port,
}
static void
+disconnect_all_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ mm_callback_info_schedule ((MMCallbackInfo *) user_data);
+}
+
+static void
+disconnect_send_cgact (MMAtSerialPort *port,
+ gint cid,
+ MMAtSerialResponseFn callback,
+ gpointer user_data)
+{
+ char *command;
+
+ if (cid >= 0)
+ command = g_strdup_printf ("+CGACT=0,%d", cid);
+ else {
+ /* Disable all PDP contexts */
+ command = g_strdup_printf ("+CGACT=0");
+ }
+
+ mm_at_serial_port_queue_command (port, command, 3, callback, user_data);
+ g_free (command);
+}
+
+#define DISCONNECT_CGACT_DONE_TAG "disconnect-cgact-done"
+
+static void
+disconnect_flash_done (MMSerialPort *port,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ /* Ignore "NO CARRIER" response when modem disconnects and any flash
+ * failures we might encounter. Other errors are hard errors.
+ */
+ if ( !g_error_matches (info->error, MM_MODEM_CONNECT_ERROR, MM_MODEM_CONNECT_ERROR_NO_CARRIER)
+ && !g_error_matches (info->error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+ g_clear_error (&info->error);
+ }
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ mm_port_set_connected (priv->data, FALSE);
+
+ /* Don't bother doing the CGACT again if it was done on a secondary port */
+ if (mm_callback_info_get_data (info, DISCONNECT_CGACT_DONE_TAG))
+ disconnect_all_done (MM_AT_SERIAL_PORT (priv->primary), NULL, NULL, info);
+ else {
+ disconnect_send_cgact (MM_AT_SERIAL_PORT (priv->primary),
+ priv->cid,
+ disconnect_all_done,
+ info);
+ }
+}
+
+static void
+disconnect_secondary_cgact_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+ MMGenericGsm *self;
+ MMGenericGsmPrivate *priv;
+
+ if (!info->modem) {
+ info->error = mm_modem_check_removed (info->modem, error);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ self = MM_GENERIC_GSM (info->modem);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ /* Now that we've tried deactivating the PDP context on the secondary
+ * port, continue with flashing the primary port.
+ */
+ if (!error)
+ mm_callback_info_set_data (info, DISCONNECT_CGACT_DONE_TAG, GUINT_TO_POINTER (TRUE), NULL);
+
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+}
+
+static void
+real_do_disconnect (MMGenericGsm *self,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ /* If the primary port is connected (with PPP) then try sending the PDP
+ * context deactivation on the secondary port because not all modems will
+ * respond to flashing (since either the modem or the kernel's serial
+ * driver doesn't support it).
+ */
+ if ( mm_port_get_connected (MM_PORT (priv->primary))
+ && priv->secondary
+ && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) {
+ disconnect_send_cgact (MM_AT_SERIAL_PORT (priv->secondary),
+ priv->cid,
+ disconnect_secondary_cgact_done,
+ info);
+ } else {
+ /* Just flash the primary port */
+ mm_serial_port_flash (MM_SERIAL_PORT (priv->primary), 1000, TRUE, disconnect_flash_done, info);
+ }
+}
+
+static void
disconnect (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
MMModemState state;
- /* First, reset the previously used CID */
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
+ priv->roam_allowed = TRUE;
info = mm_callback_info_new (modem, callback, user_data);
@@ -1423,7 +2566,9 @@ disconnect (MMModem *modem,
NULL);
mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE);
- mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info);
+
+ g_assert (MM_GENERIC_GSM_GET_CLASS (self)->do_disconnect);
+ MM_GENERIC_GSM_GET_CLASS (self)->do_disconnect (self, priv->cid, disconnect_done, info);
}
static void
@@ -1438,7 +2583,7 @@ gsm_network_scan_invoke (MMCallbackInfo *info)
}
static void
-scan_done (MMSerialPort *port,
+scan_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1470,30 +2615,33 @@ scan (MMModemGsmNetwork *modem,
G_CALLBACK (callback),
user_data);
- mm_serial_port_queue_command (priv->primary, "+COPS=?", 120, scan_done, info);
+ mm_at_serial_port_queue_command (priv->primary, "+COPS=?", 120, scan_done, info);
}
/* SetApn */
+#define APN_CID_TAG "generic-gsm-cid"
+
static void
-set_apn_done (MMSerialPort *port,
+set_apn_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- if (error)
- info->error = g_error_copy (error);
- else
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem),
- GPOINTER_TO_UINT (mm_callback_info_get_data (info, "cid")));
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ priv->cid = GPOINTER_TO_INT (mm_callback_info_get_data (info, APN_CID_TAG));
+ }
mm_callback_info_schedule (info);
}
static void
-cid_range_read (MMSerialPort *port,
+cid_range_read (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1503,7 +2651,7 @@ cid_range_read (MMSerialPort *port,
if (error)
info->error = g_error_copy (error);
- else if (g_str_has_prefix (response->str, "+CGDCONT: ")) {
+ else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
GRegex *r;
GMatchInfo *match_info;
@@ -1550,16 +2698,16 @@ cid_range_read (MMSerialPort *port,
const char *apn = (const char *) mm_callback_info_get_data (info, "apn");
char *command;
- mm_callback_info_set_data (info, "cid", GUINT_TO_POINTER (cid), NULL);
+ mm_callback_info_set_data (info, APN_CID_TAG, GINT_TO_POINTER (cid), NULL);
command = g_strdup_printf ("+CGDCONT=%d,\"IP\",\"%s\"", cid, apn);
- mm_serial_port_queue_command (port, command, 3, set_apn_done, info);
+ mm_at_serial_port_queue_command (port, command, 3, set_apn_done, info);
g_free (command);
}
}
static void
-existing_apns_read (MMSerialPort *port,
+existing_apns_read (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1567,9 +2715,10 @@ existing_apns_read (MMSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean found = FALSE;
- if (error)
- info->error = g_error_copy (error);
- else if (g_str_has_prefix (response->str, "+CGDCONT: ")) {
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+ else if (g_str_has_prefix (response->str, "+CGDCONT:")) {
GRegex *r;
GMatchInfo *match_info;
@@ -1592,7 +2741,7 @@ existing_apns_read (MMSerialPort *port,
apn = g_match_info_fetch (match_info, 3);
if (!strcmp (apn, new_apn)) {
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), (guint32) num_cid);
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->cid = num_cid;
found = TRUE;
}
@@ -1620,11 +2769,13 @@ existing_apns_read (MMSerialPort *port,
MM_MODEM_ERROR_GENERAL,
"Could not parse the response");
+done:
if (found || info->error)
mm_callback_info_schedule (info);
- else
+ else {
/* APN not configured on the card. Get the allowed CID range */
- mm_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info);
+ }
}
static void
@@ -1640,31 +2791,96 @@ set_apn (MMModemGsmNetwork *modem,
mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free);
/* Start by searching if the APN is already in card */
- mm_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info);
+ mm_at_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info);
}
/* GetSignalQuality */
+static gboolean
+emit_signal_quality_change (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->signal_quality_id = 0;
+ priv->signal_quality_timestamp = time (NULL);
+ mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), priv->signal_quality);
+ return FALSE;
+}
+
+void
+mm_generic_gsm_update_signal_quality (MMGenericGsm *self, guint32 quality)
+{
+ MMGenericGsmPrivate *priv;
+ guint delay = 0;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+ g_return_if_fail (quality <= 100);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (priv->signal_quality == quality)
+ return;
+
+ priv->signal_quality = quality;
+
+ /* Some modems will send unsolcited signal quality changes quite often,
+ * so rate-limit them to every few seconds. Track the last time we
+ * emitted signal quality so that we send the signal immediately if there
+ * haven't been any updates in a while.
+ */
+ if (!priv->signal_quality_id) {
+ if (priv->signal_quality_timestamp > 0) {
+ time_t curtime;
+ long int diff;
+
+ curtime = time (NULL);
+ diff = curtime - priv->signal_quality_timestamp;
+ if (diff == 0) {
+ /* If the device is sending more than one update per second,
+ * make sure we don't spam clients with signals.
+ */
+ delay = 3;
+ } else if ((diff > 0) && (diff <= 3)) {
+ /* Emitted an update less than 3 seconds ago; schedule an update
+ * 3 seconds after the previous one.
+ */
+ delay = (guint) diff;
+ } else {
+ /* Otherwise, we haven't emitted an update in the last 3 seconds,
+ * or the user turned their clock back, or something like that.
+ */
+ delay = 0;
+ }
+ }
+
+ priv->signal_quality_id = g_timeout_add_seconds (delay,
+ emit_signal_quality_change,
+ self);
+ }
+}
+
static void
-get_signal_quality_done (MMSerialPort *port,
+get_signal_quality_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char *reply = response->str;
+ gboolean parsed = FALSE;
- if (error)
- info->error = g_error_copy (error);
- else if (!strncmp (reply, "+CSQ: ", 6)) {
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error)
+ goto done;
+
+ if (!strncmp (reply, "+CSQ: ", 6)) {
/* Got valid reply */
int quality;
int ber;
- reply += 6;
-
- if (sscanf (reply, "%d, %d", &quality, &ber)) {
+ if (sscanf (reply + 6, "%d, %d", &quality, &ber)) {
/* 99 means unknown */
if (quality == 99) {
info->error = g_error_new_literal (MM_MOBILE_ERROR,
@@ -1674,15 +2890,19 @@ get_signal_quality_done (MMSerialPort *port,
/* Normalize the quality */
quality = CLAMP (quality, 0, 31) * 100 / 31;
- priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- priv->signal_quality = quality;
+ mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (info->modem), quality);
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
}
- } else
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Could not parse signal quality results");
+ parsed = TRUE;
+ }
}
+ if (!parsed && !info->error) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse signal quality results");
+ }
+
+done:
mm_callback_info_schedule (info);
}
@@ -1693,24 +2913,362 @@ get_signal_quality (MMModemGsmNetwork *modem,
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
- gboolean connected;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), NULL);
+ if (port)
+ mm_at_serial_port_queue_command (port, "+CSQ", 3, get_signal_quality_done, info);
+ else {
+ /* Use cached signal quality */
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL);
+ mm_callback_info_schedule (info);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMModemGsmAccessTech mm_act;
+ gint etsi_act;
+} ModeEtsi;
+
+static ModeEtsi modes_table[] = {
+ { MM_MODEM_GSM_ACCESS_TECH_GSM, 0 },
+ { MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT, 1 },
+ { MM_MODEM_GSM_ACCESS_TECH_UMTS, 2 },
+ { MM_MODEM_GSM_ACCESS_TECH_EDGE, 3 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSDPA, 4 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSUPA, 5 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSPA, 6 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSPA, 7 }, /* E-UTRAN/LTE => HSPA for now */
+ { MM_MODEM_GSM_ACCESS_TECH_UNKNOWN, -1 },
+};
+
+static MMModemGsmAccessTech
+etsi_act_to_mm_act (gint act)
+{
+ ModeEtsi *iter = &modes_table[0];
+
+ while (iter->mm_act != MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) {
+ if (iter->etsi_act == act)
+ return iter->mm_act;
+ iter++;
+ }
+ return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+}
+
+static void
+_internal_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (modem != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ g_return_if_fail (act >= MM_MODEM_GSM_ACCESS_TECH_UNKNOWN && act <= MM_MODEM_GSM_ACCESS_TECH_LAST);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (act != priv->act) {
+ MMModemDeprecatedMode old_mode;
+
+ priv->act = act;
+ g_object_notify (G_OBJECT (modem), MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+
+ /* Deprecated value */
+ old_mode = mm_modem_gsm_network_act_to_old_mode (act);
+ g_signal_emit_by_name (G_OBJECT (modem), "network-mode", old_mode);
+ }
+}
+
+void
+mm_generic_gsm_update_access_technology (MMGenericGsm *self,
+ MMModemGsmAccessTech act)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ /* For plugins, don't update the access tech when the modem isn't enabled */
+ if (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_ENABLED)
+ _internal_update_access_technology (self, act);
+}
+
+void
+mm_generic_gsm_update_allowed_mode (MMGenericGsm *self,
+ MMModemGsmAllowedMode mode)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (mode != priv->allowed_mode) {
+ priv->allowed_mode = mode;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_NETWORK_ALLOWED_MODE);
+ }
+}
+
+static void
+set_allowed_mode_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ MMModemGsmAllowedMode mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"));
- connected = mm_port_get_connected (MM_PORT (priv->primary));
- if (connected && !priv->secondary) {
- g_message ("Returning saved signal quality %d", priv->signal_quality);
- callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data);
+ mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (info->modem), mode);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMModemGsmNetwork *net,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (net);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ if (!MM_GENERIC_GSM_GET_CLASS (self)->set_allowed_mode) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ } else {
+ mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL);
+ MM_GENERIC_GSM_GET_CLASS (self)->set_allowed_mode (self, mode, set_allowed_mode_done, info);
+ }
+ break;
+ default:
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode.");
+ break;
+ }
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+/* Charset stuff */
+
+static void
+get_charsets_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
return;
}
- info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_serial_port_queue_command (connected ? priv->secondary : priv->primary, "+CSQ", 3, get_signal_quality_done, info);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ priv->charsets = MM_MODEM_CHARSET_UNKNOWN;
+ if (!mm_gsm_parse_cscs_support_response (response->str, &priv->charsets)) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Failed to parse the supported character sets response");
+ } else
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+get_supported_charsets (MMModem *modem,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+
+ /* Use cached value if we have one */
+ if (priv->charsets) {
+ mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->charsets), NULL);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Otherwise hit up the modem */
+ port = mm_generic_gsm_get_best_at_port (self, &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "+CSCS=?", 3, get_charsets_done, info);
+}
+
+static void
+set_get_charset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+ MMModemCharset tried_charset;
+ const char *p;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ p = response->str;
+ if (g_str_has_prefix (p, "+CSCS:"))
+ p += 6;
+ while (*p == ' ')
+ p++;
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ priv->cur_charset = mm_modem_charset_from_string (p);
+
+ tried_charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset"));
+
+ if (tried_charset != priv->cur_charset) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Modem failed to change character set to %s",
+ mm_modem_charset_to_string (tried_charset));
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+#define TRIED_NO_QUOTES_TAG "tried-no-quotes"
+
+static void
+set_charset_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->error) {
+ gboolean tried_no_quotes = !!mm_callback_info_get_data (info, TRIED_NO_QUOTES_TAG);
+ MMModemCharset charset = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "charset"));
+ char *command;
+
+ if (!info->modem || tried_no_quotes) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Some modems puke if you include the quotes around the character
+ * set name, so lets try it again without them.
+ */
+ mm_callback_info_set_data (info, TRIED_NO_QUOTES_TAG, GUINT_TO_POINTER (TRUE), NULL);
+ command = g_strdup_printf ("+CSCS=%s", mm_modem_charset_to_string (charset));
+ mm_at_serial_port_queue_command (port, command, 3, set_charset_done, info);
+ g_free (command);
+ } else
+ mm_at_serial_port_queue_command (port, "+CSCS?", 3, set_get_charset_done, info);
+}
+
+static gboolean
+check_for_single_value (guint32 value)
+{
+ gboolean found = FALSE;
+ guint32 i;
+
+ for (i = 1; i <= 32; i++) {
+ if (value & 0x1) {
+ if (found)
+ return FALSE; /* More than one bit set */
+ found = TRUE;
+ }
+ value >>= 1;
+ }
+
+ return TRUE;
+}
+
+static void
+set_charset (MMModem *modem,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMCallbackInfo *info;
+ const char *str;
+ char *command;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ if (!(priv->charsets & charset) || !check_for_single_value (charset)) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Character set 0x%X not supported",
+ charset);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ str = mm_modem_charset_to_string (charset);
+ if (!str) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_UNSUPPORTED_CHARSET,
+ "Unhandled character set 0x%X",
+ charset);
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_callback_info_set_data (info, "charset", GUINT_TO_POINTER (charset), NULL);
+
+ command = g_strdup_printf ("+CSCS=\"%s\"", str);
+ mm_at_serial_port_queue_command (port, command, 3, set_charset_done, info);
+ g_free (command);
+}
+
+MMModemCharset
+mm_generic_gsm_get_charset (MMGenericGsm *self)
+{
+ g_return_val_if_fail (self != NULL, MM_MODEM_CHARSET_UNKNOWN);
+ g_return_val_if_fail (MM_IS_GENERIC_GSM (self), MM_MODEM_CHARSET_UNKNOWN);
+
+ return MM_GENERIC_GSM_GET_PRIVATE (self)->cur_charset;
}
/*****************************************************************************/
/* MMModemGsmSms interface */
static void
-sms_send_done (MMSerialPort *port,
+sms_send_done (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1733,39 +3291,29 @@ sms_send (MMModemGsmSms *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv;
MMCallbackInfo *info;
char *command;
- gboolean connected;
- MMSerialPort *port = NULL;
+ MMAtSerialPort *port;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
- connected = mm_port_get_connected (MM_PORT (priv->primary));
- if (connected)
- port = priv->secondary;
- else
- port = priv->primary;
-
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
if (!port) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot send SMS while connected");
mm_callback_info_schedule (info);
return;
}
/* FIXME: use the PDU mode instead */
- mm_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL);
+ mm_at_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL);
command = g_strdup_printf ("+CMGS=\"%s\"\r%s\x1a", number, text);
- mm_serial_port_queue_command (port, command, 10, sms_send_done, info);
+ mm_at_serial_port_queue_command (port, command, 10, sms_send_done, info);
g_free (command);
}
-MMSerialPort *
-mm_generic_gsm_get_port (MMGenericGsm *modem,
- MMPortType ptype)
+MMAtSerialPort *
+mm_generic_gsm_get_at_port (MMGenericGsm *modem,
+ MMPortType ptype)
{
g_return_val_if_fail (MM_IS_GENERIC_GSM (modem), NULL);
g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL);
@@ -1778,105 +3326,267 @@ mm_generic_gsm_get_port (MMGenericGsm *modem,
return NULL;
}
+MMAtSerialPort *
+mm_generic_gsm_get_best_at_port (MMGenericGsm *self, GError **error)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_GENERIC_GSM (self), NULL);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (!mm_port_get_connected (MM_PORT (priv->primary)))
+ return priv->primary;
+
+ if (!priv->secondary) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
+ "Cannot perform this operation while connected");
+ }
+
+ return priv->secondary;
+}
+
/*****************************************************************************/
/* MMModemSimple interface */
typedef enum {
- SIMPLE_STATE_BEGIN = 0,
+ SIMPLE_STATE_CHECK_PIN = 0,
SIMPLE_STATE_ENABLE,
- SIMPLE_STATE_CHECK_PIN,
+ SIMPLE_STATE_ALLOWED_MODE,
SIMPLE_STATE_REGISTER,
SIMPLE_STATE_SET_APN,
SIMPLE_STATE_CONNECT,
SIMPLE_STATE_DONE
} SimpleState;
-static const char *
-simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+/* Looks a value up in the simple connect properties dictionary. If the
+ * requested key is not present in the dict, NULL is returned. If the
+ * requested key is present but is not a string, an error is returned.
+ */
+static gboolean
+simple_get_property (MMCallbackInfo *info,
+ const char *name,
+ GType expected_type,
+ const char **out_str,
+ guint32 *out_num,
+ gboolean *out_bool,
+ GError **error)
{
GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties");
GValue *value;
+ gint foo;
+
+ g_return_val_if_fail (properties != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ if (out_str)
+ g_return_val_if_fail (*out_str == NULL, FALSE);
value = (GValue *) g_hash_table_lookup (properties, name);
if (!value)
- return NULL;
-
- if (G_VALUE_HOLDS_STRING (value))
- return g_value_get_string (value);
+ return FALSE;
+
+ if ((expected_type == G_TYPE_STRING) && G_VALUE_HOLDS_STRING (value)) {
+ *out_str = g_value_get_string (value);
+ return TRUE;
+ } else if (expected_type == G_TYPE_UINT) {
+ if (G_VALUE_HOLDS_UINT (value)) {
+ *out_num = g_value_get_uint (value);
+ return TRUE;
+ } else if (G_VALUE_HOLDS_INT (value)) {
+ /* handle ints for convenience, but only if they are >= 0 */
+ foo = g_value_get_int (value);
+ if (foo >= 0) {
+ *out_num = (guint) foo;
+ return TRUE;
+ }
+ }
+ } else if (expected_type == G_TYPE_BOOLEAN && G_VALUE_HOLDS_BOOLEAN (value)) {
+ *out_bool = g_value_get_boolean (value);
+ return TRUE;
+ }
g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
- "Invalid property type for '%s': %s (string expected)",
- name, G_VALUE_TYPE_NAME (value));
+ "Invalid property type for '%s': %s (%s expected)",
+ name, G_VALUE_TYPE_NAME (value), g_type_name (expected_type));
- return NULL;
+ return FALSE;
+}
+
+static const char *
+simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error)
+{
+ const char *str = NULL;
+
+ simple_get_property (info, name, G_TYPE_STRING, &str, NULL, NULL, error);
+ return str;
+}
+
+static gboolean
+simple_get_uint_property (MMCallbackInfo *info, const char *name, guint32 *out_val, GError **error)
+{
+ return simple_get_property (info, name, G_TYPE_UINT, NULL, out_val, NULL, error);
+}
+
+static gboolean
+simple_get_bool_property (MMCallbackInfo *info, const char *name, gboolean *out_val, GError **error)
+{
+ return simple_get_property (info, name, G_TYPE_BOOLEAN, NULL, NULL, out_val, error);
+}
+
+static gboolean
+simple_get_allowed_mode (MMCallbackInfo *info,
+ MMModemGsmAllowedMode *out_mode,
+ GError **error)
+{
+ MMModemDeprecatedMode old_mode = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY;
+ MMModemGsmAllowedMode allowed_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ GError *tmp_error = NULL;
+
+ /* check for new allowed mode first */
+ if (simple_get_uint_property (info, "allowed_mode", &allowed_mode, &tmp_error)) {
+ if (allowed_mode > MM_MODEM_GSM_ALLOWED_MODE_LAST) {
+ g_set_error (&tmp_error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid allowed mode %d", old_mode);
+ } else {
+ *out_mode = allowed_mode;
+ return TRUE;
+ }
+ } else if (!tmp_error) {
+ /* and if not, the old allowed mode */
+ if (simple_get_uint_property (info, "network_mode", &old_mode, &tmp_error)) {
+ if (old_mode > MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST) {
+ g_set_error (&tmp_error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Invalid allowed mode %d", old_mode);
+ } else {
+ *out_mode = mm_modem_gsm_network_old_mode_to_allowed (old_mode);
+ return TRUE;
+ }
+ }
+ }
+
+ if (error)
+ *error = tmp_error;
+ return FALSE;
}
static void
simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *str;
+ MMGenericGsmPrivate *priv;
+ const char *str, *unlock = NULL;
SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
- gboolean need_pin = FALSE;
+ SimpleState next_state = state;
+ gboolean done = FALSE;
+ MMModemGsmAllowedMode allowed_mode;
+ gboolean home_only = FALSE;
- if (error) {
- if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_SIM_PIN)) {
- need_pin = TRUE;
- state = SIMPLE_STATE_CHECK_PIN;
- } else {
- info->error = g_error_copy (error);
- goto out;
- }
+ info->error = mm_modem_check_removed (modem, error);
+ if (info->error)
+ goto out;
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+ char *data_device;
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): simple connect state %d",
+ tv.tv_sec, tv.tv_usec, data_device, state);
+ g_free (data_device);
}
switch (state) {
- case SIMPLE_STATE_BEGIN:
- state = SIMPLE_STATE_ENABLE;
- mm_modem_enable (modem, simple_state_machine, info);
- break;
- case SIMPLE_STATE_ENABLE:
- state = SIMPLE_STATE_CHECK_PIN;
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (modem), simple_state_machine, info);
- break;
case SIMPLE_STATE_CHECK_PIN:
- if (need_pin) {
- str = simple_get_string_property (info, "pin", &info->error);
- if (str)
- mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info);
- else
- info->error = g_error_copy (error);
- } else {
- str = simple_get_string_property (info, "network_id", &info->error);
- state = SIMPLE_STATE_REGISTER;
- if (!info->error)
- mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ next_state = SIMPLE_STATE_ENABLE;
+
+ /* If we need a PIN, send it now, but we don't care about SIM-PIN2/SIM-PUK2
+ * since the device is operational without it.
+ */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock && strcmp (unlock, "sim-puk2") && strcmp (unlock, "sim-pin2")) {
+ gboolean success = FALSE;
+
+ if (!strcmp (unlock, "sim-pin")) {
+ str = simple_get_string_property (info, "pin", &info->error);
+ if (str) {
+ mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info);
+ success = TRUE;
+ }
+ }
+ if (!success && !info->error)
+ info->error = error_for_unlock_required (unlock);
+ break;
}
+ /* Fall through if no PIN required */
+ case SIMPLE_STATE_ENABLE:
+ next_state = SIMPLE_STATE_ALLOWED_MODE;
+ mm_modem_enable (modem, simple_state_machine, info);
break;
+ case SIMPLE_STATE_ALLOWED_MODE:
+ next_state = SIMPLE_STATE_REGISTER;
+ if ( simple_get_allowed_mode (info, &allowed_mode, &info->error)
+ && (allowed_mode != priv->allowed_mode)) {
+ mm_modem_gsm_network_set_allowed_mode (MM_MODEM_GSM_NETWORK (modem),
+ allowed_mode,
+ simple_state_machine,
+ info);
+ break;
+ } else if (info->error)
+ break;
+ /* otherwise fall through as no allowed mode was sent */
case SIMPLE_STATE_REGISTER:
+ next_state = SIMPLE_STATE_SET_APN;
+ str = simple_get_string_property (info, "network_id", &info->error);
+ if (info->error)
+ str = NULL;
+ mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ break;
+ case SIMPLE_STATE_SET_APN:
+ next_state = SIMPLE_STATE_CONNECT;
str = simple_get_string_property (info, "apn", &info->error);
- if (str) {
- state = SIMPLE_STATE_SET_APN;
- mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ if (str || info->error) {
+ if (str)
+ mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
break;
}
- /* Fall through */
- case SIMPLE_STATE_SET_APN:
- str = simple_get_string_property (info, "number", &info->error);
- state = SIMPLE_STATE_CONNECT;
- mm_modem_connect (modem, str, simple_state_machine, info);
- break;
+ /* Fall through if no APN or no 'apn' property error */
case SIMPLE_STATE_CONNECT:
- state = SIMPLE_STATE_DONE;
+ next_state = SIMPLE_STATE_DONE;
+ str = simple_get_string_property (info, "number", &info->error);
+ if (!info->error) {
+ if (simple_get_bool_property (info, "home_only", &home_only, &info->error)) {
+ MMModemGsmNetworkRegStatus status;
+
+ priv->roam_allowed = !home_only;
+
+ /* Don't connect if we're not supposed to be roaming */
+ status = gsm_reg_status (MM_GENERIC_GSM (modem));
+ if (home_only && (status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)) {
+ info->error = g_error_new_literal (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED,
+ "Roaming is not allowed.");
+ break;
+ }
+ } else if (info->error)
+ break;
+
+ mm_modem_connect (modem, str, simple_state_machine, info);
+ }
break;
case SIMPLE_STATE_DONE:
+ done = TRUE;
break;
}
out:
- if (info->error || state == SIMPLE_STATE_DONE)
+ if (info->error || done)
mm_callback_info_schedule (info);
else
- mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL);
+ mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (next_state), NULL);
}
static void
@@ -1887,6 +3597,29 @@ simple_connect (MMModemSimple *simple,
{
MMCallbackInfo *info;
+ /* If debugging, list all the simple connect properties */
+ if (mm_options_debug ()) {
+ GHashTableIter iter;
+ gpointer key, value;
+ GTimeVal tv;
+ char *data_device;
+
+ g_object_get (G_OBJECT (simple), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_get_current_time (&tv);
+
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ char *val_str;
+
+ val_str = g_strdup_value_contents ((GValue *) value);
+ g_debug ("<%ld.%ld> (%s): %s => %s",
+ tv.tv_sec, tv.tv_usec,
+ data_device, (const char *) key, val_str);
+ g_free (val_str);
+ }
+ g_free (data_device);
+ }
+
info = mm_callback_info_new (MM_MODEM (simple), callback, user_data);
mm_callback_info_set_data (info, "simple-connect-properties",
g_hash_table_ref (properties),
@@ -1895,8 +3628,6 @@ simple_connect (MMModemSimple *simple,
simple_state_machine (MM_MODEM (simple), NULL, info);
}
-
-
static void
simple_free_gvalue (gpointer data)
{
@@ -1928,16 +3659,22 @@ simple_string_value (const char *str)
return val;
}
+#define SS_HASH_TAG "simple-get-status"
+
static void
simple_status_got_signal_quality (MMModem *modem,
guint32 result,
GError *error,
gpointer user_data)
{
- if (error)
- g_warning ("Error getting signal quality: %s", error->message);
- else
- g_hash_table_insert ((GHashTable *) user_data, "signal_quality", simple_uint_value (result));
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
+
+ if (!error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "signal_quality", simple_uint_value (result));
+ }
+ mm_callback_info_chain_complete_one (info);
}
static void
@@ -1946,20 +3683,14 @@ simple_status_got_band (MMModem *modem,
GError *error,
gpointer user_data)
{
- /* Ignore band errors since there's no generic implementation for it */
- if (!error)
- g_hash_table_insert ((GHashTable *) user_data, "band", simple_uint_value (result));
-}
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ GHashTable *properties;
-static void
-simple_status_got_mode (MMModem *modem,
- guint32 result,
- GError *error,
- gpointer user_data)
-{
- /* Ignore network mode errors since there's no generic implementation for it */
- if (!error)
- g_hash_table_insert ((GHashTable *) user_data, "network_mode", simple_uint_value (result));
+ if (!error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+ g_hash_table_insert (properties, "band", simple_uint_value (result));
+ }
+ mm_callback_info_chain_complete_one (info);
}
static void
@@ -1973,17 +3704,15 @@ simple_status_got_reg_info (MMModemGsmNetwork *modem,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
GHashTable *properties;
- if (error)
- info->error = g_error_copy (error);
- else {
- properties = (GHashTable *) mm_callback_info_get_data (info, "simple-get-status");
-
+ info->error = mm_modem_check_removed ((MMModem *) modem, error);
+ if (!info->error) {
+ properties = (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG);
+
g_hash_table_insert (properties, "registration_status", simple_uint_value (status));
g_hash_table_insert (properties, "operator_code", simple_string_value (oper_code));
g_hash_table_insert (properties, "operator_name", simple_string_value (oper_name));
}
-
- mm_callback_info_schedule (info);
+ mm_callback_info_chain_complete_one (info);
}
static void
@@ -1992,7 +3721,7 @@ simple_get_status_invoke (MMCallbackInfo *info)
MMModemSimpleGetStatusFn callback = (MMModemSimpleGetStatusFn) info->callback;
callback (MM_MODEM_SIMPLE (info->modem),
- (GHashTable *) mm_callback_info_get_data (info, "simple-get-status"),
+ (GHashTable *) mm_callback_info_get_data (info, SS_HASH_TAG),
info->error, info->user_data);
}
@@ -2001,23 +3730,54 @@ simple_get_status (MMModemSimple *simple,
MMModemSimpleGetStatusFn callback,
gpointer user_data)
{
- MMModemGsmNetwork *gsm;
+ MMModemGsmNetwork *gsm = MM_MODEM_GSM_NETWORK (simple);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (simple);
GHashTable *properties;
MMCallbackInfo *info;
+ MMModemDeprecatedMode old_mode;
info = mm_callback_info_new_full (MM_MODEM (simple),
simple_get_status_invoke,
G_CALLBACK (callback),
user_data);
- properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue);
- mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref);
+ properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, simple_free_gvalue);
+ mm_callback_info_set_data (info, SS_HASH_TAG, properties, (GDestroyNotify) g_hash_table_unref);
+
+ mm_callback_info_chain_start (info, 3);
+ mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, info);
+ mm_modem_gsm_network_get_band (gsm, simple_status_got_band, info);
+ mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, info);
+
+ if (priv->act > -1) {
+ /* Deprecated key */
+ old_mode = mm_modem_gsm_network_act_to_old_mode (priv->act);
+ g_hash_table_insert (properties, "network_mode", simple_uint_value (old_mode));
+
+ /* New key */
+ g_hash_table_insert (properties, "access_technology", simple_uint_value (priv->act));
+ }
+}
+
+/*****************************************************************************/
+
+static void
+modem_state_changed (MMGenericGsm *self, GParamSpec *pspec, gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMModemState state;
- gsm = MM_MODEM_GSM_NETWORK (simple);
- mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, properties);
- mm_modem_gsm_network_get_band (gsm, simple_status_got_band, properties);
- mm_modem_gsm_network_get_mode (gsm, simple_status_got_mode, properties);
- mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, properties);
+ /* Start polling registration status and signal quality when enabled */
+
+ state = mm_modem_get_state (MM_MODEM (self));
+ if (state >= MM_MODEM_STATE_ENABLED) {
+ if (!priv->poll_id)
+ priv->poll_id = g_timeout_add_seconds (30, periodic_poll_cb, self);
+ } else {
+ if (priv->poll_id)
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
}
/*****************************************************************************/
@@ -2033,6 +3793,8 @@ modem_init (MMModem *modem_class)
modem_class->connect = connect;
modem_class->disconnect = disconnect;
modem_class->get_info = get_card_info;
+ modem_class->get_supported_charsets = get_supported_charsets;
+ modem_class->set_charset = set_charset;
}
static void
@@ -2040,10 +3802,12 @@ modem_gsm_card_init (MMModemGsmCard *class)
{
class->get_imei = get_imei;
class->get_imsi = get_imsi;
+ class->get_operator_id = get_operator_id;
class->send_pin = send_pin;
class->send_puk = send_puk;
class->enable_pin = enable_pin;
class->change_pin = change_pin;
+ class->get_unlock_retries = get_unlock_retries;
}
static void
@@ -2051,6 +3815,7 @@ modem_gsm_network_init (MMModemGsmNetwork *class)
{
class->do_register = do_register;
class->get_registration_info = get_registration_info;
+ class->set_allowed_mode = set_allowed_mode;
class->set_apn = set_apn;
class->scan = scan;
class->get_signal_quality = get_signal_quality;
@@ -2072,6 +3837,22 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_gsm_init (MMGenericGsm *self)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ priv->reg_regex = mm_gsm_creg_regex_get (TRUE);
+ priv->roam_allowed = TRUE;
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
+ MM_MODEM_GSM_NETWORK_DBUS_INTERFACE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY,
+ MM_MODEM_GSM_NETWORK_DBUS_INTERFACE);
+
+ g_signal_connect (self, "notify::" MM_MODEM_STATE,
+ G_CALLBACK (modem_state_changed), NULL);
}
static void
@@ -2086,6 +3867,8 @@ set_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL:
case MM_GENERIC_GSM_PROP_SUPPORTED_BANDS:
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
+ case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
+ case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2123,7 +3906,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, "");
break;
case MM_GENERIC_GSM_PROP_INIT_CMD:
- g_value_set_string (value, "Z E0 V1 +CMEE=1");
+ g_value_set_string (value, "Z E0 V1");
break;
case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL:
g_value_set_string (value, "X4 &C1");
@@ -2134,6 +3917,15 @@ get_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
g_value_set_uint (value, 0);
break;
+ case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
+ g_value_set_uint (value, priv->allowed_mode);
+ break;
+ case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
+ if (mm_modem_get_state (MM_MODEM (object)) >= MM_MODEM_STATE_ENABLED)
+ g_value_set_uint (value, priv->act);
+ else
+ g_value_set_uint (value, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2147,6 +3939,23 @@ finalize (GObject *object)
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (object));
+ if (priv->pin_check_timeout) {
+ g_source_remove (priv->pin_check_timeout);
+ priv->pin_check_timeout = 0;
+ }
+
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ if (priv->signal_quality_id) {
+ g_source_remove (priv->signal_quality_id);
+ priv->signal_quality_id = 0;
+ }
+
+ mm_gsm_creg_regex_destroy (priv->reg_regex);
+
g_free (priv->oper_code);
g_free (priv->oper_name);
@@ -2167,6 +3976,8 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
object_class->finalize = finalize;
klass->do_enable = real_do_enable;
+ klass->do_enable_power_up_done = real_do_enable_power_up_done;
+ klass->do_disconnect = real_do_disconnect;
/* Properties */
g_object_class_override_property (object_class,
@@ -2185,6 +3996,14 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
MM_GENERIC_GSM_PROP_SUPPORTED_MODES,
MM_MODEM_GSM_CARD_SUPPORTED_MODES);
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_ALLOWED_MODE,
+ MM_MODEM_GSM_NETWORK_ALLOWED_MODE);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY,
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+
g_object_class_install_property
(object_class, MM_GENERIC_GSM_PROP_POWER_UP_CMD,
g_param_spec_string (MM_GENERIC_GSM_POWER_UP_CMD,
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index de0b00b..de9dc89 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_GENERIC_GSM_H
@@ -20,8 +20,9 @@
#include "mm-modem-gsm.h"
#include "mm-modem-gsm-network.h"
#include "mm-modem-base.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
#include "mm-callback-info.h"
+#include "mm-charsets.h"
#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ())
#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm))
@@ -43,9 +44,16 @@ typedef enum {
MM_GENERIC_GSM_PROP_INIT_CMD,
MM_GENERIC_GSM_PROP_SUPPORTED_BANDS,
MM_GENERIC_GSM_PROP_SUPPORTED_MODES,
- MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL
+ MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL,
+ MM_GENERIC_GSM_PROP_ALLOWED_MODE,
+ MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY
} MMGenericGsmProp;
+typedef enum {
+ MM_GENERIC_GSM_REG_TYPE_UNKNOWN = 0,
+ MM_GENERIC_GSM_REG_TYPE_CS = 1,
+ MM_GENERIC_GSM_REG_TYPE_PS = 2
+} MMGenericGsmRegType;
typedef struct {
MMModemBase parent;
@@ -59,9 +67,49 @@ typedef struct {
* that need to perform custom initialization sequences or other setup should
* generally override this method instead of the MMModem interface's enable()
* method, unless the customization must happen *after* the generic init
- * sequence has completed.
+ * sequence has completed. When the subclass' enable attempt is complete
+ * the subclass should call mm_generic_gsm_enable_complete() with any error
+ * encountered during the process and the MMCallbackInfo created from the
+ * callback and user_data passed in here.
*/
void (*do_enable) (MMGenericGsm *self, MMModemFn callback, gpointer user_data);
+
+ /* Called after the generic class has attempted to power up the modem.
+ * Subclasses can handle errors here if they know the device supports their
+ * power up command. Will only be called if the device does *not* override
+ * the MMModem enable() command or allows the generic class' do_enable()
+ * handler to execute.
+ */
+ void (*do_enable_power_up_done) (MMGenericGsm *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info);
+
+ /* Called to terminate the active data call and deactivate the given PDP
+ * context.
+ */
+ void (*do_disconnect) (MMGenericGsm *self,
+ gint cid,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to set the allowed operating mode of the device */
+ void (*set_allowed_mode) (MMGenericGsm *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to get the allowed operating mode of the device */
+ void (*get_allowed_mode) (MMGenericGsm *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to the current radio access technology the
+ * device is using while communicating with the base station.
+ */
+ void (*get_access_technology) (MMGenericGsm *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
} MMGenericGsmClass;
GType mm_generic_gsm_get_type (void);
@@ -74,24 +122,42 @@ MMModem *mm_generic_gsm_new (const char *device,
#define MM_GENERIC_GSM_PREV_STATE_TAG "prev-state"
-void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
- gboolean enabled);
-
-void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
+void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
-void mm_generic_gsm_set_cid (MMGenericGsm *modem,
- guint32 cid);
+gint mm_generic_gsm_get_cid (MMGenericGsm *modem);
-guint32 mm_generic_gsm_get_cid (MMGenericGsm *modem);
void mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
+ MMGenericGsmRegType reg_type,
MMModemGsmNetworkRegStatus status);
-void mm_generic_gsm_check_pin (MMGenericGsm *modem,
- MMModemFn callback,
- gpointer user_data);
+MMModemCharset mm_generic_gsm_get_charset (MMGenericGsm *modem);
+
+/* Called to asynchronously update the current allowed operating mode that the
+ * device is allowed to use when connecting to a network. This isn't the
+ * specific access technology the device is currently using (see
+ * mm_generic_gsm_set_access_technology() for that) but the mode the device is
+ * allowed to choose from when connecting.
+ */
+void mm_generic_gsm_update_allowed_mode (MMGenericGsm *modem,
+ MMModemGsmAllowedMode mode);
+
+/* Called to asynchronously update the current access technology of the device;
+ * this is NOT the 2G/3G mode preference, but the current radio access
+ * technology being used to communicate with the base station.
+ */
+void mm_generic_gsm_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act);
+
+/* Called to asynchronously update the current signal quality of the device;
+ * 'quality' is a 0 - 100% quality.
+ */
+void mm_generic_gsm_update_signal_quality (MMGenericGsm *modem, guint32 quality);
+
+MMAtSerialPort *mm_generic_gsm_get_at_port (MMGenericGsm *modem,
+ MMPortType ptype);
-MMSerialPort *mm_generic_gsm_get_port (MMGenericGsm *modem,
- MMPortType ptype);
+MMAtSerialPort *mm_generic_gsm_get_best_at_port (MMGenericGsm *modem,
+ GError **error);
MMPort *mm_generic_gsm_grab_port (MMGenericGsm *modem,
const char *subsys,
diff --git a/src/mm-manager.c b/src/mm-manager.c
index 1a93170..1dd1902 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -11,10 +11,11 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include <string.h>
+#include <ctype.h>
#include <gmodule.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
@@ -52,6 +53,21 @@ typedef struct {
GHashTable *supports;
} MMManagerPrivate;
+typedef struct {
+ MMManager *manager;
+ char *subsys;
+ char *name;
+ char *physdev_path;
+ GSList *plugins;
+ GSList *cur_plugin;
+ guint defer_id;
+ guint done_id;
+
+ guint32 best_level;
+ MMPlugin *best_plugin;
+} SupportsInfo;
+
+
static MMPlugin *
load_plugin (const char *path)
{
@@ -188,44 +204,98 @@ remove_modem (MMManager *manager, MMModem *modem)
}
static void
-modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
+check_export_modem (MMManager *self, MMModem *modem)
{
- MMManager *manager = MM_MANAGER (user_data);
- MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- static guint32 id = 0;
- char *path, *device;
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ char *modem_physdev;
+ GHashTableIter iter;
+ gpointer value;
+
+ /* A modem is only exported to D-Bus when both of the following are true:
+ *
+ * 1) the modem is valid
+ * 2) all ports the modem provides have either been grabbed or are
+ * unsupported by any plugin
+ *
+ * This ensures that all the modem's ports are completely ready before
+ * any clients can do anything with it.
+ *
+ * FIXME: if udev or the kernel are really slow giving us ports, there's a
+ * chance that a port could show up after the modem is already created and
+ * all other ports are already handled. That chance is very small though.
+ */
+ modem_physdev = mm_modem_get_device (modem);
+ g_assert (modem_physdev);
+
+ /* Check for ports that are in the process of being interrogated by plugins */
+ g_hash_table_iter_init (&iter, priv->supports);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ SupportsInfo *info = value;
+
+ if (!strcmp (info->physdev_path, modem_physdev)) {
+ g_debug ("(%s/%s): outstanding support task prevents export of %s",
+ info->subsys, info->name, modem_physdev);
+ goto out;
+ }
+ }
+
+ /* Already exported? This can happen if the modem is exported and the kernel
+ * discovers another of the modem's ports.
+ */
+ if (g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG))
+ goto out;
+
+ /* No outstanding port tasks, so if the modem is valid we can export it */
if (mm_modem_get_valid (modem)) {
+ static guint32 id = 0;
+ char *path, *data_device = NULL;
+
path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++);
dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem));
g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free);
- device = mm_modem_get_device (modem);
- g_assert (device);
- g_debug ("Exported modem %s as %s", device, path);
- g_free (device);
+ g_debug ("Exported modem %s as %s", modem_physdev, path);
- g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
- } else
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_debug ("(%s): data port is %s", path, data_device);
+ g_free (data_device);
+
+ g_signal_emit (self, signals[DEVICE_ADDED], 0, modem);
+ }
+
+out:
+ g_free (modem_physdev);
+}
+
+static void
+modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+ MMManager *manager = MM_MANAGER (user_data);
+
+ if (mm_modem_get_valid (modem))
+ check_export_modem (manager, modem);
+ else
remove_modem (manager, modem);
}
+#define MANAGER_PLUGIN_TAG "manager-plugin"
+
static void
-add_modem (MMManager *manager, MMModem *modem)
+add_modem (MMManager *manager, MMModem *modem, MMPlugin *plugin)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
char *device;
- gboolean valid = FALSE;
device = mm_modem_get_device (modem);
g_assert (device);
if (!g_hash_table_lookup (priv->modems, device)) {
g_hash_table_insert (priv->modems, g_strdup (device), modem);
+ g_object_set_data (G_OBJECT (modem), MANAGER_PLUGIN_TAG, plugin);
+
g_debug ("Added modem %s", device);
- g_signal_connect (modem, "notify::valid", G_CALLBACK (modem_valid), manager);
- g_object_get (modem, MM_MODEM_VALID, &valid, NULL);
- if (valid)
- modem_valid (modem, NULL, manager);
+ g_signal_connect (modem, "notify::" MM_MODEM_VALID, G_CALLBACK (modem_valid), manager);
+ check_export_modem (manager, modem);
}
g_free (device);
}
@@ -236,10 +306,8 @@ enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data)
MMModem *modem = MM_MODEM (val);
GPtrArray **devices = (GPtrArray **) user_data;
const char *path;
- gboolean valid = FALSE;
- g_object_get (G_OBJECT (modem), MM_MODEM_VALID, &valid, NULL);
- if (valid) {
+ if (mm_modem_get_valid (modem)) {
path = g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG);
g_return_if_fail (path != NULL);
g_ptr_array_add (*devices, g_strdup (path));
@@ -265,15 +333,18 @@ find_modem_for_device (MMManager *manager, const char *device)
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
GHashTableIter iter;
gpointer key, value;
+ MMModem *found = NULL;
g_hash_table_iter_init (&iter, priv->modems);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- MMModem *modem = MM_MODEM (value);
+ while (g_hash_table_iter_next (&iter, &key, &value) && !found) {
+ MMModem *candidate = MM_MODEM (value);
+ char *candidate_device = mm_modem_get_device (candidate);
- if (!strcmp (device, mm_modem_get_device (modem)))
- return modem;
+ if (!strcmp (device, candidate_device))
+ found = candidate;
+ g_free (candidate_device);
}
- return NULL;
+ return found;
}
@@ -294,21 +365,11 @@ find_modem_for_port (MMManager *manager, const char *subsys, const char *name)
return NULL;
}
-typedef struct {
- MMManager *manager;
- char *subsys;
- char *name;
- GSList *plugins;
- GSList *cur_plugin;
- guint defer_id;
- guint done_id;
-
- guint32 best_level;
- MMPlugin *best_plugin;
-} SupportsInfo;
-
static SupportsInfo *
-supports_info_new (MMManager *self, const char *subsys, const char *name)
+supports_info_new (MMManager *self,
+ const char *subsys,
+ const char *name,
+ const char *physdev_path)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
SupportsInfo *info;
@@ -317,6 +378,7 @@ supports_info_new (MMManager *self, const char *subsys, const char *name)
info->manager = self;
info->subsys = g_strdup (subsys);
info->name = g_strdup (name);
+ info->physdev_path = g_strdup (physdev_path);
info->plugins = g_slist_copy (priv->plugins);
info->cur_plugin = info->plugins;
return info;
@@ -357,20 +419,21 @@ static void supports_callback (MMPlugin *plugin,
static void try_supports_port (MMManager *manager,
MMPlugin *plugin,
- const char *subsys,
- const char *name,
+ MMModem *existing,
SupportsInfo *info);
static gboolean
supports_defer_timeout (gpointer user_data)
{
SupportsInfo *info = user_data;
+ MMModem *existing;
+
+ existing = find_modem_for_device (info->manager, info->physdev_path);
g_debug ("(%s): re-checking support...", info->name);
try_supports_port (info->manager,
MM_PLUGIN (info->cur_plugin->data),
- info->subsys,
- info->name,
+ existing,
info);
return FALSE;
}
@@ -378,23 +441,30 @@ supports_defer_timeout (gpointer user_data)
static void
try_supports_port (MMManager *manager,
MMPlugin *plugin,
- const char *subsys,
- const char *name,
+ MMModem *existing,
SupportsInfo *info)
{
MMPluginSupportsResult result;
- result = mm_plugin_supports_port (plugin, subsys, name, supports_callback, info);
+ result = mm_plugin_supports_port (plugin,
+ info->subsys,
+ info->name,
+ info->physdev_path,
+ existing,
+ supports_callback,
+ info);
switch (result) {
case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
/* If the plugin knows it doesn't support the modem, just call the
* callback and indicate 0 support.
*/
- supports_callback (plugin, subsys, name, 0, info);
+ supports_callback (plugin, info->subsys, info->name, 0, info);
break;
case MM_PLUGIN_SUPPORTS_PORT_DEFER:
- g_debug ("(%s): (%s) deferring support check", mm_plugin_get_name (plugin), name);
+ g_debug ("(%s): (%s) deferring support check",
+ mm_plugin_get_name (plugin),
+ info->name);
if (info->defer_id)
g_source_remove (info->defer_id);
@@ -407,23 +477,54 @@ try_supports_port (MMManager *manager,
}
}
+static void
+supports_cleanup (MMManager *self,
+ const char *subsys,
+ const char *name,
+ MMModem *modem)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ char *key;
+
+ g_return_if_fail (subsys != NULL);
+ g_return_if_fail (name != NULL);
+
+ key = get_key (subsys, name);
+ g_hash_table_remove (priv->supports, key);
+ g_free (key);
+
+ /* Each time a supports task is cleaned up, check whether the modem is
+ * now completely probed/handled and should be exported to D-Bus clients.
+ *
+ * IMPORTANT: this must be done after removing the supports into from
+ * priv->supports since check_export_modem() searches through priv->supports
+ * for outstanding supports tasks.
+ */
+ if (modem)
+ check_export_modem (self, modem);
+}
+
static gboolean
do_grab_port (gpointer user_data)
{
SupportsInfo *info = user_data;
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager);
- MMModem *modem;
+ MMModem *modem = NULL;
GError *error = NULL;
- char *key;
GSList *iter;
/* No more plugins to try */
if (info->best_plugin) {
+ MMModem *existing;
+
+ existing = g_hash_table_lookup (priv->modems, info->physdev_path);
+
/* Create the modem */
- modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, &error);
+ modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, existing, &error);
if (modem) {
guint32 modem_type = MM_MODEM_TYPE_UNKNOWN;
const char *type_name = "UNKNOWN";
+ char *device;;
g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL);
if (modem_type == MM_MODEM_TYPE_GSM)
@@ -431,13 +532,15 @@ do_grab_port (gpointer user_data)
else if (modem_type == MM_MODEM_TYPE_CDMA)
type_name = "CDMA";
+ device = mm_modem_get_device (modem);
g_message ("(%s): %s modem %s claimed port %s",
mm_plugin_get_name (info->best_plugin),
type_name,
- mm_modem_get_device (modem),
+ device,
info->name);
+ g_free (device);
- add_modem (info->manager, modem);
+ add_modem (info->manager, modem, info->best_plugin);
} else {
g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
__func__,
@@ -446,6 +549,7 @@ do_grab_port (gpointer user_data)
info->name,
error ? error->code : -1,
(error && error->message) ? error->message : "(unknown)");
+ modem = existing;
}
}
@@ -455,10 +559,7 @@ do_grab_port (gpointer user_data)
g_slist_free (info->plugins);
info->cur_plugin = info->plugins = NULL;
- key = get_key (info->subsys, info->name);
- g_hash_table_remove (priv->supports, key);
- g_free (key);
-
+ supports_cleanup (info->manager, info->subsys, info->name, modem);
return FALSE;
}
@@ -471,10 +572,7 @@ supports_callback (MMPlugin *plugin,
{
SupportsInfo *info = user_data;
MMPlugin *next_plugin = NULL;
-
- info->cur_plugin = info->cur_plugin->next;
- if (info->cur_plugin)
- next_plugin = MM_PLUGIN (info->cur_plugin->data);
+ MMModem *existing;
/* Is this plugin's result better than any one we've tried before? */
if (level > info->best_level) {
@@ -482,32 +580,147 @@ supports_callback (MMPlugin *plugin,
info->best_plugin = plugin;
}
- /* Prevent the generic plugin from probing devices that are already supported
- * by other plugins. For Huawei for example, secondary ports shouldn't
- * be probed, but the generic plugin would happily do so if allowed to.
+ /* If there's already a modem for this port's physical device, stop asking
+ * plugins because the same plugin that owns the modem gets this port no
+ * matter what.
*/
- if ( next_plugin
- && !strcmp (mm_plugin_get_name (next_plugin), MM_PLUGIN_GENERIC_NAME)
- && info->best_plugin)
- next_plugin = NULL;
+ existing = find_modem_for_device (info->manager, info->physdev_path);
+ if (existing) {
+ MMPlugin *existing_plugin;
+
+ existing_plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
+ g_assert (existing_plugin);
+
+ if (plugin == existing_plugin) {
+ if (level == 0) {
+ /* If the plugin that just completed the support check claims not to
+ * support this port, but this plugin is clearly the right plugin
+ * since it claimed this port's physical modem, just drop the port.
+ */
+ g_debug ("(%s/%s): ignoring port unsupported by physical modem's plugin",
+ info->subsys, info->name);
+ supports_cleanup (info->manager, info->subsys, info->name, existing);
+ return;
+ }
+
+ /* Otherwise, this port was supported by the plugin that owns the
+ * port's physical modem, so we stop the supports checks anyway.
+ */
+ next_plugin = NULL;
+ } else if (info->best_plugin != existing_plugin) {
+ /* If this port hasn't yet been handled by the right plugin, stop
+ * asking all other plugins if they support this port, just let the
+ * plugin that handles this port's physical device see if it
+ * supports it.
+ */
+ next_plugin = existing_plugin;
+ } else {
+ g_debug ("(%s/%s): plugin %p (%s) existing %p (%s) info->best %p (%s)",
+ info->subsys, info->name,
+ plugin,
+ plugin ? mm_plugin_get_name (plugin) : "none",
+ existing_plugin,
+ existing_plugin ? mm_plugin_get_name (existing_plugin) : "none",
+ info->best_plugin,
+ info->best_plugin ? mm_plugin_get_name (info->best_plugin) : "none");
+ g_assert_not_reached ();
+ }
+ } else {
+ info->cur_plugin = g_slist_next (info->cur_plugin);
+ if (info->cur_plugin)
+ next_plugin = MM_PLUGIN (info->cur_plugin->data);
+ }
+
+ /* Don't bother with Generic if some other plugin already supports this port */
+ if (next_plugin) {
+ const char *next_name = mm_plugin_get_name (next_plugin);
+
+ if (info->best_plugin && !strcmp (next_name, MM_PLUGIN_GENERIC_NAME))
+ next_plugin = NULL;
+ }
if (next_plugin) {
/* Try the next plugin */
- try_supports_port (info->manager, next_plugin, info->subsys, info->name, info);
+ try_supports_port (info->manager, next_plugin, existing, info);
} else {
/* All done; let the best modem grab the port */
info->done_id = g_idle_add (do_grab_port, info);
}
}
+static GUdevDevice *
+find_physical_device (GUdevDevice *child)
+{
+ GUdevDevice *iter, *old = NULL;
+ GUdevDevice *physdev = NULL;
+ const char *subsys, *type;
+ guint32 i = 0;
+ gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
+
+ g_return_val_if_fail (child != NULL, NULL);
+
+ iter = g_object_ref (child);
+ while (iter && i++ < 8) {
+ subsys = g_udev_device_get_subsystem (iter);
+ if (subsys) {
+ if (is_usb || !strcmp (subsys, "usb")) {
+ is_usb = TRUE;
+ type = g_udev_device_get_devtype (iter);
+ if (type && !strcmp (type, "usb_device")) {
+ physdev = iter;
+ break;
+ }
+ } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
+ GUdevDevice *pcmcia_parent;
+ const char *tmp_subsys;
+
+ is_pcmcia = TRUE;
+
+ /* If the parent of this PCMCIA device is no longer part of
+ * the PCMCIA subsystem, we want to stop since we're looking
+ * for the base PCMCIA device, not the PCMCIA controller which
+ * is usually PCI or some other bus type.
+ */
+ pcmcia_parent = g_udev_device_get_parent (iter);
+ if (pcmcia_parent) {
+ tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
+ if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
+ physdev = iter;
+ g_object_unref (pcmcia_parent);
+ if (physdev)
+ break;
+ }
+ } else if (is_platform || !strcmp (subsys, "platform")) {
+ /* Take the first platform parent as the physical device */
+ is_platform = TRUE;
+ physdev = iter;
+ break;
+ } else if (is_pci || !strcmp (subsys, "pci")) {
+ is_pci = TRUE;
+ physdev = iter;
+ break;
+ }
+ }
+
+ old = iter;
+ iter = g_udev_device_get_parent (old);
+ g_object_unref (old);
+ }
+
+ return physdev;
+}
+
static void
device_added (MMManager *manager, GUdevDevice *device)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- const char *subsys, *name;
+ const char *subsys, *name, *physdev_path, *physdev_subsys;
SupportsInfo *info;
char *key;
gboolean found;
+ GUdevDevice *physdev = NULL;
+ MMPlugin *plugin;
+ MMModem *existing;
g_return_if_fail (device != NULL);
@@ -517,20 +730,77 @@ device_added (MMManager *manager, GUdevDevice *device)
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
+ /* ignore VTs */
+ if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
+ return;
+
if (find_modem_for_port (manager, subsys, name))
return;
key = get_key (subsys, name);
found = !!g_hash_table_lookup (priv->supports, key);
- if (found) {
- g_free (key);
- return;
+ if (found)
+ goto out;
+
+ /* Find the port's physical device's sysfs path. This is the kernel device
+ * that "owns" all the ports of the device, like the USB device or the PCI
+ * device the provides each tty or network port.
+ */
+ physdev = find_physical_device (device);
+ if (!physdev) {
+ /* Warn about it, but filter out some common ports that we know don't have
+ * anything to do with mobile broadband.
+ */
+ if ( strcmp (name, "console")
+ && strcmp (name, "ptmx")
+ && strcmp (name, "lo")
+ && strcmp (name, "tty")
+ && !strstr (name, "virbr"))
+ g_debug ("(%s/%s): could not get port's parent device", subsys, name);
+
+ goto out;
+ }
+
+ /* Is the device blacklisted? */
+ if (g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_IGNORE")) {
+ g_debug ("(%s/%s): port's parent device is blacklisted", subsys, name);
+ goto out;
+ }
+
+ /* If the physdev is a 'platform' device that's not whitelisted, ignore it */
+ physdev_subsys = g_udev_device_get_subsystem (physdev);
+ if ( physdev_subsys
+ && !strcmp (physdev_subsys, "platform")
+ && !g_udev_device_get_property_as_boolean (physdev, "ID_MM_PLATFORM_DRIVER_PROBE")) {
+ g_debug ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
+ goto out;
}
- info = supports_info_new (manager, subsys, name);
- g_hash_table_insert (priv->supports, key, info);
+ physdev_path = g_udev_device_get_sysfs_path (physdev);
+ if (!physdev_path) {
+ g_debug ("(%s/%s): could not get port's parent device sysfs path", subsys, name);
+ goto out;
+ }
+
+ /* Success; now ask plugins if they can handle this port */
+ info = supports_info_new (manager, subsys, name, physdev_path);
+ g_hash_table_insert (priv->supports, g_strdup (key), info);
+
+ /* If this port's physical modem is already owned by a plugin, don't bother
+ * asking all plugins whether they support this port, just let the owning
+ * plugin check if it supports the port.
+ */
+ plugin = MM_PLUGIN (info->cur_plugin->data);
+ existing = find_modem_for_device (manager, physdev_path);
+ if (existing)
+ plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
- try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info);
+ try_supports_port (manager, plugin, existing, info);
+
+out:
+ if (physdev)
+ g_object_unref (physdev);
+ g_free (key);
}
static void
@@ -628,12 +898,83 @@ mm_manager_start (MMManager *manager)
priv = MM_MANAGER_GET_PRIVATE (manager);
devices = g_udev_client_query_by_subsystem (priv->udev, "tty");
- for (iter = devices; iter; iter = g_list_next (iter))
+ for (iter = devices; iter; iter = g_list_next (iter)) {
device_added (manager, G_UDEV_DEVICE (iter->data));
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
devices = g_udev_client_query_by_subsystem (priv->udev, "net");
- for (iter = devices; iter; iter = g_list_next (iter))
+ for (iter = devices; iter; iter = g_list_next (iter)) {
device_added (manager, G_UDEV_DEVICE (iter->data));
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
+}
+
+typedef struct {
+ MMManager *manager;
+ MMModem *modem;
+} RemoveInfo;
+
+static gboolean
+remove_disable_one (gpointer user_data)
+{
+ RemoveInfo *info = user_data;
+
+ remove_modem (info->manager, info->modem);
+ g_free (info);
+ return FALSE;
+}
+
+static void
+remove_disable_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ RemoveInfo *info;
+
+ /* Schedule modem removal from an idle handler since we get here deep
+ * in the modem removal callchain and can't remove it quite yet from here.
+ */
+ info = g_malloc0 (sizeof (RemoveInfo));
+ info->manager = MM_MANAGER (user_data);
+ info->modem = modem;
+ g_idle_add (remove_disable_one, info);
+}
+
+void
+mm_manager_shutdown (MMManager *self)
+{
+ GList *modems, *iter;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MANAGER (self));
+
+ modems = g_hash_table_get_values (MM_MANAGER_GET_PRIVATE (self)->modems);
+ for (iter = modems; iter; iter = g_list_next (iter)) {
+ MMModem *modem = MM_MODEM (iter->data);
+
+ if (mm_modem_get_state (modem) >= MM_MODEM_STATE_ENABLING)
+ mm_modem_disable (modem, remove_disable_done, self);
+ else
+ remove_disable_done (modem, NULL, self);
+ }
+ g_list_free (modems);
+
+ /* Disabling may take a few iterations of the mainloop, so the caller
+ * has to iterate the mainloop until all devices have been disabled and
+ * removed.
+ */
+}
+
+guint32
+mm_manager_num_modems (MMManager *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_MANAGER (self), 0);
+
+ return g_hash_table_size (MM_MANAGER_GET_PRIVATE (self)->modems);
}
static void
diff --git a/src/mm-manager.h b/src/mm-manager.h
index 5220b77..1c98458 100644
--- a/src/mm-manager.h
+++ b/src/mm-manager.h
@@ -50,4 +50,8 @@ MMManager *mm_manager_new (DBusGConnection *bus);
void mm_manager_start (MMManager *manager);
+void mm_manager_shutdown (MMManager *manager);
+
+guint32 mm_manager_num_modems (MMManager *manager);
+
#endif /* MM_MANAGER_H */
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index 3d82f8e..0c39d2c 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -21,10 +21,13 @@
#include "mm-modem-base.h"
#include "mm-modem.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-errors.h"
#include "mm-options.h"
#include "mm-properties-changed-signal.h"
+#include "mm-callback-info.h"
+#include "mm-modem-helpers.h"
static void modem_init (MMModem *modem_class);
@@ -39,10 +42,19 @@ typedef struct {
char *driver;
char *plugin;
char *device;
+ char *equipment_ident;
+ char *unlock_required;
+ guint32 unlock_retries;
guint32 ip_method;
gboolean valid;
MMModemState state;
+ char *manf;
+ char *model;
+ char *revision;
+
+ MMAuthProvider *authp;
+
GHashTable *ports;
} MMModemBasePrivate;
@@ -92,7 +104,7 @@ mm_modem_base_add_port (MMModemBase *self,
{
MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
MMPort *port = NULL;
- char *key;
+ char *key, *device;
g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
g_return_val_if_fail (subsys != NULL, NULL);
@@ -111,9 +123,12 @@ mm_modem_base_add_port (MMModemBase *self,
g_return_val_if_fail (port == NULL, FALSE);
}
- if (!strcmp (subsys, "tty"))
- port = MM_PORT (mm_serial_port_new (name, ptype));
- else if (!strcmp (subsys, "net")) {
+ if (!strcmp (subsys, "tty")) {
+ if (ptype == MM_PORT_TYPE_QCDM)
+ port = MM_PORT (mm_qcdm_serial_port_new (name, ptype));
+ else
+ port = MM_PORT (mm_at_serial_port_new (name, ptype));
+ } else if (!strcmp (subsys, "net")) {
port = MM_PORT (g_object_new (MM_TYPE_PORT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET,
@@ -124,6 +139,15 @@ mm_modem_base_add_port (MMModemBase *self,
if (!port)
return NULL;
+ if (mm_options_debug ()) {
+ device = mm_modem_get_device (MM_MODEM (self));
+
+ g_message ("(%s) type %s claimed by %s",
+ name,
+ mm_port_type_to_name (ptype),
+ device);
+ g_free (device);
+ }
key = get_hash_key (subsys, name);
g_hash_table_insert (priv->ports, key, port);
return port;
@@ -169,6 +193,362 @@ mm_modem_base_get_valid (MMModemBase *self)
return MM_MODEM_BASE_GET_PRIVATE (self)->valid;
}
+const char *
+mm_modem_base_get_equipment_identifier (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->equipment_ident;
+}
+
+void
+mm_modem_base_set_equipment_identifier (MMModemBase *self, const char *ident)
+{
+ MMModemBasePrivate *priv;
+ const char *dbus_path;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Only do something if the value changes */
+ if ( (priv->equipment_ident == ident)
+ || (priv->equipment_ident && ident && !strcmp (priv->equipment_ident, ident)))
+ return;
+
+ g_free (priv->equipment_ident);
+ priv->equipment_ident = g_strdup (ident);
+
+ dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ if (dbus_path) {
+ if (priv->equipment_ident)
+ g_message ("Modem %s: Equipment identifier set (%s)", dbus_path, priv->equipment_ident);
+ else
+ g_message ("Modem %s: Equipment identifier not set", dbus_path);
+ }
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_EQUIPMENT_IDENTIFIER);
+}
+
+const char *
+mm_modem_base_get_unlock_required (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->unlock_required;
+}
+
+void
+mm_modem_base_set_unlock_required (MMModemBase *self, const char *unlock_required)
+{
+ MMModemBasePrivate *priv;
+ const char *dbus_path;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Only do something if the value changes */
+ if ( (priv->unlock_required == unlock_required)
+ || ( priv->unlock_required
+ && unlock_required
+ && !strcmp (priv->unlock_required, unlock_required)))
+ return;
+
+ g_free (priv->unlock_required);
+ priv->unlock_required = g_strdup (unlock_required);
+
+ dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ if (dbus_path) {
+ if (priv->unlock_required)
+ g_message ("Modem %s: unlock required (%s)", dbus_path, priv->unlock_required);
+ else
+ g_message ("Modem %s: unlock no longer required", dbus_path);
+ }
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_REQUIRED);
+}
+
+guint32
+mm_modem_base_get_unlock_retries (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), 0);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->unlock_retries;
+}
+
+void
+mm_modem_base_set_unlock_retries (MMModemBase *self, guint unlock_retries)
+{
+ MMModemBasePrivate *priv;
+ const char *dbus_path;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Only do something if the value changes */
+ if (priv->unlock_retries == unlock_retries)
+ return;
+
+ priv->unlock_retries = unlock_retries;
+
+ dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
+ if (dbus_path) {
+ g_message ("Modem %s: # unlock retries for %s is %d",
+ dbus_path, priv->unlock_required, priv->unlock_retries);
+ }
+
+ g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_RETRIES);
+}
+
+const char *
+mm_modem_base_get_manf (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->manf;
+}
+
+void
+mm_modem_base_set_manf (MMModemBase *self, const char *manf)
+{
+ MMModemBasePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ g_free (priv->manf);
+ priv->manf = g_strdup (manf);
+}
+
+const char *
+mm_modem_base_get_model (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->model;
+}
+
+void
+mm_modem_base_set_model (MMModemBase *self, const char *model)
+{
+ MMModemBasePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ g_free (priv->model);
+ priv->model = g_strdup (model);
+}
+
+const char *
+mm_modem_base_get_revision (MMModemBase *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM_BASE (self), NULL);
+
+ return MM_MODEM_BASE_GET_PRIVATE (self)->revision;
+}
+
+void
+mm_modem_base_set_revision (MMModemBase *self, const char *revision)
+{
+ MMModemBasePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ g_free (priv->revision);
+ priv->revision = g_strdup (revision);
+}
+
+/*************************************************************************/
+static void
+card_info_simple_invoke (MMCallbackInfo *info)
+{
+ MMModemBase *self = MM_MODEM_BASE (info->modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ MMModemInfoFn callback = (MMModemInfoFn) info->callback;
+
+ callback (info->modem, priv->manf, priv->model, priv->revision, info->error, info->user_data);
+}
+
+static void
+card_info_cache_invoke (MMCallbackInfo *info)
+{
+ MMModemBase *self = MM_MODEM_BASE (info->modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ MMModemInfoFn callback = (MMModemInfoFn) info->callback;
+ const char *manf, *cmanf, *model, *cmodel, *rev, *crev;
+
+ manf = mm_callback_info_get_data (info, "card-info-manf");
+ cmanf = mm_callback_info_get_data (info, "card-info-c-manf");
+
+ model = mm_callback_info_get_data (info, "card-info-model");
+ cmodel = mm_callback_info_get_data (info, "card-info-c-model");
+
+ rev = mm_callback_info_get_data (info, "card-info-revision");
+ crev = mm_callback_info_get_data (info, "card-info-c-revision");
+
+ /* Prefer the 'C' responses over the plain responses */
+ g_free (priv->manf);
+ priv->manf = g_strdup (cmanf ? cmanf : manf);
+ g_free (priv->model);
+ priv->model = g_strdup (cmodel ? cmodel : model);
+ g_free (priv->revision);
+ priv->revision = g_strdup (crev ? crev : rev);
+
+ callback (info->modem, priv->manf, priv->model, priv->revision, info->error, info->user_data);
+}
+
+static void
+info_item_done (MMCallbackInfo *info,
+ GString *response,
+ GError *error,
+ const char *tag,
+ const char *desc)
+{
+ const char *p;
+
+ if (!error) {
+ p = mm_strip_tag (response->str, tag);
+ mm_callback_info_set_data (info, desc, strlen (p) ? g_strdup (p) : NULL, g_free);
+ }
+
+ mm_callback_info_chain_complete_one (info);
+}
+
+#define GET_INFO_RESP_FN(func_name, tag, desc) \
+static void \
+func_name (MMAtSerialPort *port, \
+ GString *response, \
+ GError *error, \
+ gpointer user_data) \
+{ \
+ info_item_done ((MMCallbackInfo *) user_data, response, error, tag , desc ); \
+}
+
+GET_INFO_RESP_FN(get_revision_done, "+GMR:", "card-info-revision")
+GET_INFO_RESP_FN(get_model_done, "+GMM:", "card-info-model")
+GET_INFO_RESP_FN(get_manf_done, "+GMI:", "card-info-manf")
+
+GET_INFO_RESP_FN(get_c_revision_done, "+CGMR:", "card-info-c-revision")
+GET_INFO_RESP_FN(get_c_model_done, "+CGMM:", "card-info-c-model")
+GET_INFO_RESP_FN(get_c_manf_done, "+CGMI:", "card-info-c-manf")
+
+void
+mm_modem_base_get_card_info (MMModemBase *self,
+ MMAtSerialPort *port,
+ GError *port_error,
+ MMModemInfoFn callback,
+ gpointer user_data)
+{
+ MMModemBasePrivate *priv;
+ MMCallbackInfo *info;
+ MMModemState state;
+ gboolean cached = FALSE;
+ GError *error = port_error;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM_BASE (self));
+ g_return_if_fail (port != NULL);
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (port));
+ g_return_if_fail (callback != NULL);
+
+ priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ /* Cached info and errors schedule the callback immediately and do
+ * not hit up the card for it's model information.
+ */
+ if (priv->manf || priv->model || priv->revision)
+ cached = TRUE;
+ else {
+ state = mm_modem_get_state (MM_MODEM (self));
+
+ if (port_error)
+ error = g_error_copy (port_error);
+ else if (state < MM_MODEM_STATE_ENABLING) {
+ error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "The modem is not enabled.");
+ }
+ }
+
+ /* If we have cached info or an error, don't hit up the card */
+ if (cached || error) {
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ card_info_simple_invoke,
+ G_CALLBACK (callback),
+ user_data);
+ info->error = error;
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* Otherwise, ask the card */
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ card_info_cache_invoke,
+ G_CALLBACK (callback),
+ user_data);
+
+ mm_callback_info_chain_start (info, 6);
+ mm_at_serial_port_queue_command_cached (port, "+GMI", 3, get_manf_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+GMM", 3, get_model_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+GMR", 3, get_revision_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGMI", 3, get_c_manf_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGMM", 3, get_c_model_done, info);
+ mm_at_serial_port_queue_command_cached (port, "+CGMR", 3, get_c_revision_done, info);
+}
+
+/*****************************************************************************/
+
+static gboolean
+modem_auth_request (MMModem *modem,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error)
+{
+ MMModemBase *self = MM_MODEM_BASE (modem);
+ MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+
+ g_assert (priv->authp);
+ return !!mm_auth_provider_request_auth (priv->authp,
+ authorization,
+ G_OBJECT (self),
+ context,
+ callback,
+ callback_data,
+ notify,
+ error);
+}
+
+static gboolean
+modem_auth_finish (MMModem *modem, MMAuthRequest *req, GError **error)
+{
+ if (mm_auth_request_get_result (req) != MM_AUTH_RESULT_AUTHORIZED) {
+ g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_AUTHORIZATION_REQUIRED,
+ "This request requires the '%s' authorization",
+ mm_auth_request_get_authorization (req));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/*****************************************************************************/
static void
@@ -176,16 +556,29 @@ mm_modem_base_init (MMModemBase *self)
{
MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ priv->authp = mm_auth_provider_get ();
+
priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
mm_properties_changed_signal_register_property (G_OBJECT (self),
MM_MODEM_ENABLED,
MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_EQUIPMENT_IDENTIFIER,
+ MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_UNLOCK_REQUIRED,
+ MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_UNLOCK_RETRIES,
+ MM_MODEM_DBUS_INTERFACE);
}
static void
modem_init (MMModem *modem_class)
{
+ modem_class->auth_request = modem_auth_request;
+ modem_class->auth_finish = modem_auth_finish;
}
static gboolean
@@ -227,6 +620,9 @@ set_property (GObject *object, guint prop_id,
case MM_MODEM_PROP_VALID:
case MM_MODEM_PROP_TYPE:
case MM_MODEM_PROP_ENABLED:
+ case MM_MODEM_PROP_EQUIPMENT_IDENTIFIER:
+ case MM_MODEM_PROP_UNLOCK_REQUIRED:
+ case MM_MODEM_PROP_UNLOCK_RETRIES:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -268,6 +664,15 @@ get_property (GObject *object, guint prop_id,
case MM_MODEM_PROP_ENABLED:
g_value_set_boolean (value, is_enabled (priv->state));
break;
+ case MM_MODEM_PROP_EQUIPMENT_IDENTIFIER:
+ g_value_set_string (value, priv->equipment_ident);
+ break;
+ case MM_MODEM_PROP_UNLOCK_REQUIRED:
+ g_value_set_string (value, priv->unlock_required);
+ break;
+ case MM_MODEM_PROP_UNLOCK_RETRIES:
+ g_value_set_uint (value, priv->unlock_retries);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -280,10 +685,14 @@ finalize (GObject *object)
MMModemBase *self = MM_MODEM_BASE (object);
MMModemBasePrivate *priv = MM_MODEM_BASE_GET_PRIVATE (self);
+ mm_auth_provider_cancel_for_owner (priv->authp, object);
+
g_hash_table_destroy (priv->ports);
g_free (priv->driver);
g_free (priv->plugin);
g_free (priv->device);
+ g_free (priv->equipment_ident);
+ g_free (priv->unlock_required);
G_OBJECT_CLASS (mm_modem_base_parent_class)->finalize (object);
}
@@ -336,6 +745,18 @@ mm_modem_base_class_init (MMModemBaseClass *klass)
MM_MODEM_PROP_ENABLED,
MM_MODEM_ENABLED);
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_EQUIPMENT_IDENTIFIER,
+ MM_MODEM_EQUIPMENT_IDENTIFIER);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_UNLOCK_REQUIRED,
+ MM_MODEM_UNLOCK_REQUIRED);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_UNLOCK_RETRIES,
+ MM_MODEM_UNLOCK_RETRIES);
+
mm_properties_changed_signal_new (object_class);
}
diff --git a/src/mm-modem-base.h b/src/mm-modem-base.h
index 9eb64ce..0409957 100644
--- a/src/mm-modem-base.h
+++ b/src/mm-modem-base.h
@@ -22,6 +22,8 @@
#include <glib-object.h>
#include "mm-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-modem.h"
#define MM_TYPE_MODEM_BASE (mm_modem_base_get_type ())
#define MM_MODEM_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_BASE, MMModemBase))
@@ -60,5 +62,36 @@ void mm_modem_base_set_valid (MMModemBase *self,
gboolean mm_modem_base_get_valid (MMModemBase *self);
+const char *mm_modem_base_get_equipment_identifier (MMModemBase *self);
+
+void mm_modem_base_set_equipment_identifier (MMModemBase *self,
+ const char *ident);
+
+const char *mm_modem_base_get_unlock_required (MMModemBase *self);
+
+void mm_modem_base_set_unlock_required (MMModemBase *self,
+ const char *unlock_required);
+
+guint mm_modem_base_get_unlock_retries (MMModemBase *self);
+
+void mm_modem_base_set_unlock_retries (MMModemBase *self,
+ guint unlock_retries);
+
+
+const char *mm_modem_base_get_manf (MMModemBase *self);
+void mm_modem_base_set_manf (MMModemBase *self, const char *manf);
+
+const char *mm_modem_base_get_model (MMModemBase *self);
+void mm_modem_base_set_model (MMModemBase *self, const char *model);
+
+const char *mm_modem_base_get_revision (MMModemBase *self);
+void mm_modem_base_set_revision (MMModemBase *self, const char *revision);
+
+void mm_modem_base_get_card_info (MMModemBase *self,
+ MMAtSerialPort *port,
+ GError *port_error,
+ MMModemInfoFn callback,
+ gpointer user_data);
+
#endif /* MM_MODEM_BASE_H */
diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c
index 112b93f..e80dc4e 100644
--- a/src/mm-modem-cdma.c
+++ b/src/mm-modem-cdma.c
@@ -20,6 +20,7 @@
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-marshal.h"
+#include "mm-auth-provider.h"
static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodInvocation *context);
static void impl_modem_cdma_get_esn (MMModemCdma *modem, DBusGMethodInvocation *context);
@@ -188,10 +189,38 @@ mm_modem_cdma_get_esn (MMModemCdma *self,
}
static void
-impl_modem_cdma_get_esn (MMModemCdma *modem,
- DBusGMethodInvocation *context)
+esn_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- mm_modem_cdma_get_esn (modem, str_call_done, context);
+ MMModemCdma *self = MM_MODEM_CDMA (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the ESN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_cdma_get_esn (self, str_call_done, context);
+}
+
+static void
+impl_modem_cdma_get_esn (MMModemCdma *self, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the ESN */
+ if (!mm_modem_auth_request (MM_MODEM (self),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ esn_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
void
diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c
index dea4590..e03b964 100644
--- a/src/mm-modem-gsm-card.c
+++ b/src/mm-modem-gsm-card.c
@@ -15,6 +15,7 @@
*/
#include <dbus/dbus-glib.h>
+#include <string.h>
#include "mm-modem-gsm-card.h"
#include "mm-errors.h"
@@ -27,6 +28,9 @@ static void impl_gsm_modem_get_imei (MMModemGsmCard *modem,
static void impl_gsm_modem_get_imsi (MMModemGsmCard *modem,
DBusGMethodInvocation *context);
+static void impl_gsm_modem_get_operator_id (MMModemGsmCard *modem,
+ DBusGMethodInvocation *context);
+
static void impl_gsm_modem_send_pin (MMModemGsmCard *modem,
const char *pin,
DBusGMethodInvocation *context);
@@ -76,6 +80,19 @@ str_call_not_supported (MMModemGsmCard *self,
}
static void
+uint_call_not_supported (MMModemGsmCard *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+static void
async_call_done (MMModem *modem, GError *error, gpointer user_data)
{
DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
@@ -129,6 +146,35 @@ mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
str_call_not_supported (self, callback, user_data);
}
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_GSM_CARD (self));
+ g_return_if_fail (pin_type != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries)
+ MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_unlock_retries (self, pin_type, callback, user_data);
+ else
+ uint_call_not_supported (self, callback, user_data);
+}
+
+void
+mm_modem_gsm_card_get_operator_id (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_GSM_CARD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_operator_id)
+ MM_MODEM_GSM_CARD_GET_INTERFACE (self)->get_operator_id (self, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+}
+
void
mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
const char *puk,
@@ -201,26 +247,213 @@ mm_modem_gsm_card_change_pin (MMModemGsmCard *self,
/*****************************************************************************/
static void
-impl_gsm_modem_get_imei (MMModemGsmCard *modem,
- DBusGMethodInvocation *context)
+imei_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- mm_modem_gsm_card_get_imei (modem, str_call_done, context);
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMEI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_get_imei (self, str_call_done, context);
+}
+
+static void
+impl_gsm_modem_get_imei (MMModemGsmCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the IMEI */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ imei_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+imsi_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMSI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_get_imsi (self, str_call_done, context);
+}
+
+static void
+impl_gsm_modem_get_imsi (MMModemGsmCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the IMSI */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ imsi_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+operator_id_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the operator id */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_get_operator_id (self, str_call_done, context);
}
static void
-impl_gsm_modem_get_imsi (MMModemGsmCard *modem,
+impl_gsm_modem_get_operator_id (MMModemGsmCard *modem, DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to get the operator id */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_INFO,
+ context,
+ operator_id_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ char *puk;
+ char *pin;
+ char *pin2;
+ gboolean enabled;
+} SendPinPukInfo;
+
+static void
+send_pin_puk_info_destroy (gpointer data)
+{
+ SendPinPukInfo *info = data;
+
+ g_free (info->puk);
+ g_free (info->pin);
+ g_free (info->pin2);
+ memset (info, 0, sizeof (SendPinPukInfo));
+ g_free (info);
+}
+
+static SendPinPukInfo *
+send_pin_puk_info_new (const char *puk,
+ const char *pin,
+ const char *pin2,
+ gboolean enabled)
+{
+ SendPinPukInfo *info;
+
+ info = g_malloc0 (sizeof (SendPinPukInfo));
+ info->puk = g_strdup (puk);
+ info->pin = g_strdup (pin);
+ info->pin2 = g_strdup (pin2);
+ info->enabled = enabled;
+ return info;
+}
+
+/*****************************************************************************/
+
+static void
+send_puk_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise send the PUK */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_send_puk (self, info->puk, info->pin, async_call_done, context);
+}
+
+static void
+impl_gsm_modem_send_puk (MMModemGsmCard *modem,
+ const char *puk,
+ const char *pin,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_get_imsi (modem, str_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (puk, pin, NULL, FALSE);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ send_puk_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
+/*****************************************************************************/
+
static void
- impl_gsm_modem_send_puk (MMModemGsmCard *modem,
- const char *puk,
- const char *pin,
- DBusGMethodInvocation *context)
+send_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- mm_modem_gsm_card_send_puk (modem, puk, pin, async_call_done, context);
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise unlock the modem */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_send_pin (self, info->pin, async_call_done, context);
}
static void
@@ -228,7 +461,42 @@ impl_gsm_modem_send_pin (MMModemGsmCard *modem,
const char *pin,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_send_pin (modem, pin, async_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (NULL, pin, NULL, FALSE);
+
+ /* Make sure the caller is authorized to unlock the modem */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ send_pin_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+enable_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise enable the PIN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_enable_pin (self, info->pin, info->enabled, async_call_done, context);
}
static void
@@ -237,7 +505,42 @@ impl_gsm_modem_enable_pin (MMModemGsmCard *modem,
gboolean enabled,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_enable_pin (modem, pin, enabled, async_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (NULL, pin, NULL, enabled);
+
+ /* Make sure the caller is authorized to enable a PIN */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ enable_pin_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+change_pin_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmCard *self = MM_MODEM_GSM_CARD (owner);
+ SendPinPukInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise change the PIN */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_card_change_pin (self, info->pin, info->pin2, async_call_done, context);
}
static void
@@ -246,7 +549,22 @@ impl_gsm_modem_change_pin (MMModemGsmCard *modem,
const char *new_pin,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_card_change_pin (modem, old_pin, new_pin, async_call_done, context);
+ GError *error = NULL;
+ SendPinPukInfo *info;
+
+ info = send_pin_puk_info_new (NULL, old_pin, new_pin, FALSE);
+
+ /* Make sure the caller is authorized to change the PIN */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ change_pin_auth_cb,
+ info,
+ send_pin_puk_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
/*****************************************************************************/
@@ -267,7 +585,7 @@ mm_modem_gsm_card_init (gpointer g_iface)
"Supported Modes",
"Supported frequency bands of the card",
MM_MODEM_GSM_BAND_UNKNOWN,
- MM_MODEM_GSM_BAND_LAST,
+ G_MAXUINT32,
MM_MODEM_GSM_BAND_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
@@ -277,7 +595,7 @@ mm_modem_gsm_card_init (gpointer g_iface)
"Supported Modes",
"Supported modes of the card (ex 2G preferred, 3G preferred, 2G only, etc",
MM_MODEM_GSM_MODE_UNKNOWN,
- MM_MODEM_GSM_MODE_LAST,
+ G_MAXUINT32,
MM_MODEM_GSM_MODE_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
@@ -305,6 +623,7 @@ mm_modem_gsm_card_get_type (void)
&card_info, 0);
g_type_interface_add_prerequisite (card_type, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (card_type, MM_TYPE_MODEM);
dbus_g_object_type_install_info (card_type, &dbus_glib_mm_modem_gsm_card_object_info);
}
diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h
index 4d690e6..584d734 100644
--- a/src/mm-modem-gsm-card.h
+++ b/src/mm-modem-gsm-card.h
@@ -27,6 +27,13 @@
#define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands"
#define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes"
+#define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin"
+#define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2"
+#define MM_MODEM_GSM_CARD_SIM_PUK "sim-puk"
+#define MM_MODEM_GSM_CARD_SIM_PUK2 "sim-puk2"
+
+#define MM_MODEM_GSM_CARD_UNLOCK_RETRIES_NOT_SUPPORTED 999
+
typedef struct _MMModemGsmCard MMModemGsmCard;
struct _MMModemGsmCard {
@@ -41,6 +48,15 @@ struct _MMModemGsmCard {
MMModemStringFn callback,
gpointer user_data);
+ void (*get_unlock_retries) (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*get_operator_id) (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
void (*send_puk) (MMModemGsmCard *self,
const char *puk,
const char *pin,
@@ -75,6 +91,15 @@ void mm_modem_gsm_card_get_imsi (MMModemGsmCard *self,
MMModemStringFn callback,
gpointer user_data);
+void mm_modem_gsm_card_get_unlock_retries (MMModemGsmCard *self,
+ const char *pin_type,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_gsm_card_get_operator_id (MMModemGsmCard *self,
+ MMModemStringFn callback,
+ gpointer user_data);
+
void mm_modem_gsm_card_send_puk (MMModemGsmCard *self,
const char *puk,
const char *pin,
diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c
index 26ff422..4cd6921 100644
--- a/src/mm-modem-gsm-network.c
+++ b/src/mm-modem-gsm-network.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2010 Red Hat, Inc.
*/
#include <string.h>
@@ -42,8 +43,12 @@ static void impl_gsm_modem_set_band (MMModemGsmNetwork *modem,
static void impl_gsm_modem_get_band (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context);
+static void impl_gsm_modem_set_allowed_mode (MMModemGsmNetwork *modem,
+ MMModemGsmAllowedMode mode,
+ DBusGMethodInvocation *context);
+
static void impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+ MMModemDeprecatedMode old_mode,
DBusGMethodInvocation *context);
static void impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem,
@@ -68,6 +73,47 @@ static guint signals[LAST_SIGNAL] = { 0 };
/*****************************************************************************/
+MMModemGsmAllowedMode
+mm_modem_gsm_network_old_mode_to_allowed (MMModemDeprecatedMode old_mode)
+{
+ /* Translate deprecated mode into new mode */
+ switch (old_mode) {
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED:
+ return MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED:
+ return MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY:
+ return MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY:
+ return MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY:
+ default:
+ return MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ }
+}
+
+MMModemDeprecatedMode
+mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act)
+{
+ /* Translate new mode into old deprecated mode */
+ if (act & MM_MODEM_GSM_ACCESS_TECH_GPRS)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_EDGE)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_UMTS)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSDPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSUPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA;
+
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY;
+}
+
+/*****************************************************************************/
+
static void
async_call_done (MMModem *modem, GError *error, gpointer user_data)
{
@@ -305,35 +351,21 @@ mm_modem_gsm_network_get_band (MMModemGsmNetwork *self,
}
void
-mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data)
+mm_modem_gsm_network_set_allowed_mode (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
g_return_if_fail (callback != NULL);
- if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode)
- MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode (self, mode, callback, user_data);
+ if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_allowed_mode)
+ MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_allowed_mode (self, mode, callback, user_data);
else
async_call_not_supported (self, callback, user_data);
}
void
-mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data)
-{
- g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
- g_return_if_fail (callback != NULL);
-
- if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode)
- MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode (self, callback, user_data);
- else
- uint_call_not_supported (self, callback, user_data);
-}
-
-void
mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data)
@@ -369,15 +401,6 @@ mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self,
oper_name ? oper_name : "");
}
-void
-mm_modem_gsm_network_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode)
-{
- g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
-
- g_signal_emit (self, signals[NETWORK_MODE], 0, mode);
-}
-
/*****************************************************************************/
static void
@@ -398,10 +421,39 @@ impl_gsm_modem_register (MMModemGsmNetwork *modem,
}
static void
+scan_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmNetwork *self = MM_MODEM_GSM_NETWORK (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the IMEI */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_network_scan (self, scan_call_done, context);
+}
+
+static void
impl_gsm_modem_scan (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_network_scan (modem, scan_call_done, context);
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to request a scan */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ scan_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
static void
@@ -464,10 +516,10 @@ impl_gsm_modem_get_band (MMModemGsmNetwork *modem,
static void
impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+ MMModemDeprecatedMode old_mode,
DBusGMethodInvocation *context)
{
- if (!check_for_single_value (mode)) {
+ if (!check_for_single_value (old_mode)) {
GError *error;
error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
@@ -477,14 +529,41 @@ impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
return;
}
- mm_modem_gsm_network_set_mode (modem, mode, async_call_done, context);
+ mm_modem_gsm_network_set_allowed_mode (modem,
+ mm_modem_gsm_network_old_mode_to_allowed (old_mode),
+ async_call_done,
+ context);
+}
+
+static void
+impl_gsm_modem_set_allowed_mode (MMModemGsmNetwork *modem,
+ MMModemGsmAllowedMode mode,
+ DBusGMethodInvocation *context)
+{
+ if (mode > MM_MODEM_GSM_ALLOWED_MODE_LAST) {
+ GError *error;
+
+ error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Unknown allowed mode %d", mode);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ mm_modem_gsm_network_set_allowed_mode (modem, mode, async_call_done, context);
}
static void
impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_network_get_mode (modem, uint_call_done, context);
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+
+ /* DEPRECATED; it's now a property so it's quite easy to handle */
+ g_object_get (G_OBJECT (modem),
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY, &act,
+ NULL);
+ dbus_g_method_return (context, mm_modem_gsm_network_act_to_old_mode (act));
}
static void
@@ -505,6 +584,28 @@ mm_modem_gsm_network_init (gpointer g_iface)
if (initialized)
return;
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
+ "Allowed Mode",
+ "Allowed network access mode",
+ MM_MODEM_GSM_ALLOWED_MODE_ANY,
+ MM_MODEM_GSM_ALLOWED_MODE_LAST,
+ MM_MODEM_GSM_ALLOWED_MODE_ANY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY,
+ "Access Technology",
+ "Current access technology in use when connected to "
+ "a mobile network.",
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN,
+ MM_MODEM_GSM_ACCESS_TECH_LAST,
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
/* Signals */
signals[SIGNAL_QUALITY] =
g_signal_new ("signal-quality",
@@ -530,8 +631,7 @@ mm_modem_gsm_network_init (gpointer g_iface)
g_signal_new ("network-mode",
iface_type,
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (MMModemGsmNetwork, network_mode),
- NULL, NULL,
+ 0, NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
@@ -562,6 +662,7 @@ mm_modem_gsm_network_get_type (void)
&network_info, 0);
g_type_interface_add_prerequisite (network_type, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (network_type, MM_TYPE_MODEM);
dbus_g_object_type_install_info (network_type, &dbus_glib_mm_modem_gsm_network_object_info);
}
diff --git a/src/mm-modem-gsm-network.h b/src/mm-modem-gsm-network.h
index 493baec..a515789 100644
--- a/src/mm-modem-gsm-network.h
+++ b/src/mm-modem-gsm-network.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_MODEM_GSM_NETWORK_H
@@ -20,11 +20,23 @@
#include <mm-modem.h>
#include <mm-modem-gsm.h>
+#define MM_MODEM_GSM_NETWORK_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Gsm.Network"
+
#define MM_TYPE_MODEM_GSM_NETWORK (mm_modem_gsm_network_get_type ())
#define MM_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork))
#define MM_IS_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_NETWORK))
#define MM_MODEM_GSM_NETWORK_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork))
+#define MM_MODEM_GSM_NETWORK_ALLOWED_MODE "allowed-mode"
+#define MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY "access-technology"
+
+typedef enum {
+ MM_MODEM_GSM_NETWORK_PROP_FIRST = 0x1200,
+
+ MM_MODEM_GSM_NETWORK_PROP_ALLOWED_MODE = MM_MODEM_GSM_NETWORK_PROP_FIRST,
+ MM_MODEM_GSM_NETWORK_PROP_ACCESS_TECHNOLOGY,
+} MMModemGsmNetworkProp;
+
typedef enum {
MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE = 0,
MM_MODEM_GSM_NETWORK_REG_STATUS_HOME = 1,
@@ -80,15 +92,11 @@ struct _MMModemGsmNetwork {
MMModemUIntFn callback,
gpointer user_data);
- void (*set_network_mode) (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
+ void (*set_allowed_mode) (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
MMModemFn callback,
gpointer user_data);
- void (*get_network_mode) (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data);
-
void (*get_registration_info) (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data);
@@ -101,9 +109,6 @@ struct _MMModemGsmNetwork {
MMModemGsmNetworkRegStatus status,
const char *open_code,
const char *oper_name);
-
- void (*network_mode) (MMModemGsmNetwork *self,
- MMModemGsmMode mode);
};
GType mm_modem_gsm_network_get_type (void);
@@ -135,15 +140,6 @@ void mm_modem_gsm_network_get_band (MMModemGsmNetwork *self,
MMModemUIntFn callback,
gpointer user_data);
-void mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data);
-
-void mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data);
-
void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data);
@@ -153,12 +149,19 @@ void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
void mm_modem_gsm_network_signal_quality (MMModemGsmNetwork *self,
guint32 quality);
+void mm_modem_gsm_network_set_allowed_mode (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
void mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegStatus status,
const char *oper_code,
const char *oper_name);
-void mm_modem_gsm_network_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode);
+/* Private */
+MMModemDeprecatedMode mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act);
+
+MMModemGsmAllowedMode mm_modem_gsm_network_old_mode_to_allowed (MMModemDeprecatedMode old_mode);
#endif /* MM_MODEM_GSM_NETWORK_H */
diff --git a/src/mm-modem-gsm-sms.c b/src/mm-modem-gsm-sms.c
index e8ec074..d74c7b3 100644
--- a/src/mm-modem-gsm-sms.c
+++ b/src/mm-modem-gsm-sms.c
@@ -129,12 +129,138 @@ mm_modem_gsm_sms_send (MMModemGsmSms *self,
/*****************************************************************************/
+typedef struct {
+ guint num1;
+ guint num2;
+ guint num3;
+ guint num4;
+ guint num5;
+ char *str;
+ GHashTable *hash;
+} SmsAuthInfo;
+
+static void
+sms_auth_info_destroy (gpointer data)
+{
+ SmsAuthInfo *info = data;
+
+ g_hash_table_destroy (info->hash);
+ g_free (info->str);
+ memset (info, 0, sizeof (SmsAuthInfo));
+ g_free (info);
+}
+
+static void
+destroy_gvalue (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static SmsAuthInfo *
+sms_auth_info_new (guint num1,
+ guint num2,
+ guint num3,
+ guint num4,
+ guint num5,
+ const char *str,
+ GHashTable *hash)
+{
+ SmsAuthInfo *info;
+
+ info = g_malloc0 (sizeof (SmsAuthInfo));
+ info->num1 = num1;
+ info->num2 = num2;
+ info->num3 = num3;
+ info->num4 = num4;
+ info->num5 = num5;
+ info->str = g_strdup (str);
+
+ /* Copy the hash table if we're given one */
+ if (hash) {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ info->hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, destroy_gvalue);
+
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const char *str_key = (const char *) key;
+ GValue *src = (GValue *) value;
+ GValue *dst;
+
+ dst = g_slice_new0 (GValue);
+ g_value_init (dst, G_VALUE_TYPE (src));
+ g_value_copy (src, dst);
+ g_hash_table_insert (info->hash, g_strdup (str_key), dst);
+ }
+ }
+
+ return info;
+}
+
+/*****************************************************************************/
+
+static void
+sms_delete_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
+}
+
static void
impl_gsm_modem_sms_delete (MMModemGsmSms *modem,
guint idx,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (idx, 0, 0, 0, 0, NULL, NULL);
+
+ /* Make sure the caller is authorized to delete an SMS */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_delete_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_get_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -142,9 +268,26 @@ impl_gsm_modem_sms_get (MMModemGsmSms *modem,
guint idx,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (idx, 0, 0, 0, 0, NULL, NULL);
+
+ /* Make sure the caller is authorized to get an SMS */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_get_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
+/*****************************************************************************/
+
static void
impl_gsm_modem_sms_get_format (MMModemGsmSms *modem,
DBusGMethodInvocation *context)
@@ -167,19 +310,103 @@ impl_gsm_modem_sms_get_smsc (MMModemGsmSms *modem,
async_call_not_supported (modem, async_call_done, context);
}
+/*****************************************************************************/
+
+static void
+sms_set_smsc_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise set the SMS service center */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
+}
+
static void
impl_gsm_modem_sms_set_smsc (MMModemGsmSms *modem,
const char *smsc,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (0, 0, 0, 0, 0, smsc, NULL);
+
+ /* Make sure the caller is authorized to set the SMS service center */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_set_smsc_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_list_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise list SMSs */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
impl_gsm_modem_sms_list (MMModemGsmSms *modem,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to list SMSs */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_list_auth_cb,
+ NULL,
+ NULL,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_save_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise save the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -187,54 +414,122 @@ impl_gsm_modem_sms_save (MMModemGsmSms *modem,
GHashTable *properties,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (0, 0, 0, 0, 0, NULL, properties);
+
+ /* Make sure the caller is authorized to save the SMS */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_save_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
+/*****************************************************************************/
+
static void
-impl_gsm_modem_sms_send (MMModemGsmSms *modem,
- GHashTable *properties,
- DBusGMethodInvocation *context)
+sms_send_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ SmsAuthInfo *info = user_data;
+ GError *error = NULL;
GValue *value;
const char *number = NULL;
const char *text = NULL ;
const char *smsc = NULL;
- GError *error = NULL;
guint validity = 0;
guint class = 0;
- value = (GValue *) g_hash_table_lookup (properties, "number");
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error))
+ goto done;
+
+ value = (GValue *) g_hash_table_lookup (info->hash, "number");
if (value)
number = g_value_get_string (value);
- value = (GValue *) g_hash_table_lookup (properties, "text");
+ value = (GValue *) g_hash_table_lookup (info->hash, "text");
if (value)
text = g_value_get_string (value);
- value = (GValue *) g_hash_table_lookup (properties, "smsc");
+ value = (GValue *) g_hash_table_lookup (info->hash, "smsc");
if (value)
smsc = g_value_get_string (value);
- value = (GValue *) g_hash_table_lookup (properties, "validity");
+ value = (GValue *) g_hash_table_lookup (info->hash, "validity");
if (value)
validity = g_value_get_uint (value);
- value = (GValue *) g_hash_table_lookup (properties, "class");
+ value = (GValue *) g_hash_table_lookup (info->hash, "class");
if (value)
class = g_value_get_uint (value);
- if (!number)
+ if (!number) {
error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Missing number");
- else if (!text)
+ } else if (!text) {
error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Missing message text");
+ }
+done:
if (error) {
- async_call_done (MM_MODEM (modem), error, context);
+ async_call_done (MM_MODEM (self), error, context);
g_error_free (error);
} else
- mm_modem_gsm_sms_send (modem, number, text, smsc, validity, class, async_call_done, context);
+ mm_modem_gsm_sms_send (self, number, text, smsc, validity, class, async_call_done, context);
+}
+
+static void
+impl_gsm_modem_sms_send (MMModemGsmSms *modem,
+ GHashTable *properties,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (0, 0, 0, 0, 0, NULL, properties);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_send_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_send_from_storage_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -242,7 +537,41 @@ impl_gsm_modem_sms_send_from_storage (MMModemGsmSms *modem,
guint idx,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (idx, 0, 0, 0, 0, NULL, NULL);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_send_from_storage_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+sms_set_indication_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmSms *self = MM_MODEM_GSM_SMS (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise delete the SMS */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ async_call_not_supported (self, async_call_done, context);
}
static void
@@ -254,7 +583,22 @@ impl_gsm_modem_sms_set_indication (MMModemGsmSms *modem,
guint bfr,
DBusGMethodInvocation *context)
{
- async_call_not_supported (modem, async_call_done, context);
+ GError *error = NULL;
+ SmsAuthInfo *info;
+
+ info = sms_auth_info_new (mode, mt, bm, ds, bfr, NULL, NULL);
+
+ /* Make sure the caller is authorized to send the PUK */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_SMS,
+ context,
+ sms_set_indication_auth_cb,
+ info,
+ sms_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
}
/*****************************************************************************/
diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h
index 852ff85..6d9135a 100644
--- a/src/mm-modem-gsm.h
+++ b/src/mm-modem-gsm.h
@@ -30,11 +30,35 @@ typedef enum {
MM_MODEM_GSM_MODE_3G_ONLY = 0x00000100,
MM_MODEM_GSM_MODE_HSUPA = 0x00000200,
MM_MODEM_GSM_MODE_HSPA = 0x00000400,
-
- MM_MODEM_GSM_MODE_LAST = MM_MODEM_GSM_MODE_HSPA
+ MM_MODEM_GSM_MODE_GSM = 0x00000800,
+ MM_MODEM_GSM_MODE_GSM_COMPACT = 0x00001000,
} MMModemGsmMode;
typedef enum {
+ MM_MODEM_GSM_ALLOWED_MODE_ANY = 0,
+ MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1,
+ MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2,
+ MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY = 3,
+ MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY = 4,
+
+ MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY
+} MMModemGsmAllowedMode;
+
+typedef enum {
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0,
+ MM_MODEM_GSM_ACCESS_TECH_GSM = 1,
+ MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 2,
+ MM_MODEM_GSM_ACCESS_TECH_GPRS = 3,
+ MM_MODEM_GSM_ACCESS_TECH_EDGE = 4, /* GSM w/EGPRS */
+ MM_MODEM_GSM_ACCESS_TECH_UMTS = 5, /* UTRAN */
+ MM_MODEM_GSM_ACCESS_TECH_HSDPA = 6, /* UTRAN w/HSDPA */
+ MM_MODEM_GSM_ACCESS_TECH_HSUPA = 7, /* UTRAN w/HSUPA */
+ MM_MODEM_GSM_ACCESS_TECH_HSPA = 8, /* UTRAN w/HSDPA and HSUPA */
+
+ MM_MODEM_GSM_ACCESS_TECH_LAST = MM_MODEM_GSM_ACCESS_TECH_HSPA
+} MMModemGsmAccessTech;
+
+typedef enum {
MM_MODEM_GSM_BAND_UNKNOWN = 0x00000000,
MM_MODEM_GSM_BAND_ANY = 0x00000001,
MM_MODEM_GSM_BAND_EGSM = 0x00000002, /* 900 MHz */
@@ -48,10 +72,26 @@ typedef enum {
MM_MODEM_GSM_BAND_U850 = 0x00000200, /* WCDMA 3GPP UMTS 850 MHz (Class V) */
MM_MODEM_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */
MM_MODEM_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */
+ MM_MODEM_GSM_BAND_U1900 = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz (Class II) */
- MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U17IX
+ MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U1900
} MMModemGsmBand;
-#endif /* MM_MODEM_GSM_H */
+typedef enum {
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA
+} MMModemDeprecatedMode;
+
+#endif /* MM_MODEM_GSM_H */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 1741b5f..6e76f46 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -14,13 +14,32 @@
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <glib.h>
#include <string.h>
#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
#include "mm-errors.h"
#include "mm-modem-helpers.h"
+const char *
+mm_strip_tag (const char *str, const char *cmd)
+{
+ const char *p = str;
+
+ if (p) {
+ if (!strncmp (p, cmd, strlen (cmd)))
+ p += strlen (cmd);
+ while (isspace (*p))
+ p++;
+ }
+ return p;
+}
+
+/*************************************************************************/
+
static void
save_scan_value (GHashTable *hash, const char *key, GMatchInfo *info, guint32 num)
{
@@ -201,3 +220,586 @@ mm_gsm_destroy_scan_data (gpointer data)
g_ptr_array_free (results, TRUE);
}
+/*************************************************************************/
+
+/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
+#define CREG1 "\\+(CREG|CGREG):\\s*(\\d{1})"
+
+/* +CREG: <n>,<stat> (GSM 07.07 CREG=1 solicited) */
+#define CREG2 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})"
+
+/* +CREG: <stat>,<lac>,<ci> (GSM 07.07 CREG=2 unsolicited) */
+#define CREG3 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+
+/* +CREG: <n>,<stat>,<lac>,<ci> (GSM 07.07 solicited and some CREG=2 unsolicited) */
+#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+
+/* +CREG: <stat>,<lac>,<ci>,<AcT> (ETSI 27.007 CREG=2 unsolicited) */
+#define CREG5 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
+
+/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
+#define CREG6 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
+
+GPtrArray *
+mm_gsm_creg_regex_get (gboolean solicited)
+{
+ GPtrArray *array = g_ptr_array_sized_new (6);
+ GRegex *regex;
+
+ /* #1 */
+ if (solicited)
+ regex = g_regex_new (CREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #2 */
+ if (solicited)
+ regex = g_regex_new (CREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #3 */
+ if (solicited)
+ regex = g_regex_new (CREG3 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG3 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #4 */
+ if (solicited)
+ regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG4 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #5 */
+ if (solicited)
+ regex = g_regex_new (CREG5 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG5 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #6 */
+ if (solicited)
+ regex = g_regex_new (CREG6 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG6 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ return array;
+}
+
+void
+mm_gsm_creg_regex_destroy (GPtrArray *array)
+{
+ g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL);
+ g_ptr_array_free (array, TRUE);
+}
+
+/*************************************************************************/
+
+static gulong
+parse_uint (char *str, int base, glong nmin, glong nmax, gboolean *valid)
+{
+ gulong ret = 0;
+ char *endquote;
+
+ *valid = FALSE;
+ if (!str)
+ return 0;
+
+ /* Strip quotes */
+ if (str[0] == '"')
+ str++;
+ endquote = strchr (str, '"');
+ if (endquote)
+ *endquote = '\0';
+
+ if (strlen (str)) {
+ ret = strtol (str, NULL, base);
+ if ((nmin == nmax) || (ret >= nmin && ret <= nmax))
+ *valid = TRUE;
+ }
+ return *valid ? (guint) ret : 0;
+}
+
+gboolean
+mm_gsm_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_cgreg,
+ GError **error)
+{
+ gboolean success = FALSE, foo;
+ gint n_matches, act = -1;
+ gulong stat = 0, lac = 0, ci = 0;
+ guint istat = 0, ilac = 0, ici = 0, iact = 0;
+ char *str;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (out_reg_state != NULL, FALSE);
+ g_return_val_if_fail (out_lac != NULL, FALSE);
+ g_return_val_if_fail (out_ci != NULL, FALSE);
+ g_return_val_if_fail (out_act != NULL, FALSE);
+ g_return_val_if_fail (out_cgreg != NULL, FALSE);
+
+ str = g_match_info_fetch (info, 1);
+ if (str && strstr (str, "CGREG"))
+ *out_cgreg = TRUE;
+
+ /* Normally the number of matches could be used to determine what each
+ * item is, but we have overlap in one case.
+ */
+ n_matches = g_match_info_get_match_count (info);
+ if (n_matches == 3) {
+ /* CREG=1: +CREG: <stat> */
+ istat = 2;
+ } else if (n_matches == 4) {
+ /* Solicited response: +CREG: <n>,<stat> */
+ istat = 3;
+ } else if (n_matches == 5) {
+ /* CREG=2 (GSM 07.07): +CREG: <stat>,<lac>,<ci> */
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ } else if (n_matches == 6) {
+ /* CREG=2 (ETSI 27.007): +CREG: <stat>,<lac>,<ci>,<AcT>
+ * CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>
+ */
+
+ /* To distinguish, check length of the third match item. If it's
+ * more than one digit or has quotes in it then it's a LAC and we
+ * got the first format.
+ */
+ str = g_match_info_fetch (info, 3);
+ if (str && (strchr (str, '"') || strlen (str) > 1)) {
+ g_free (str);
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ iact = 5;
+ } else {
+ istat = 3;
+ ilac = 4;
+ ici = 5;
+ }
+ } else if (n_matches == 7) {
+ /* CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>,<AcT> */
+ istat = 3;
+ ilac = 4;
+ ici = 5;
+ iact = 6;
+ }
+
+ /* Status */
+ str = g_match_info_fetch (info, istat);
+ stat = parse_uint (str, 10, 0, 5, &success);
+ g_free (str);
+ if (!success) {
+ g_set_error_literal (error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse the registration status response");
+ return FALSE;
+ }
+
+ /* Location Area Code */
+ if (ilac) {
+ /* FIXME: some phones apparently swap the LAC bytes (LG, SonyEricsson,
+ * Sagem). Need to handle that.
+ */
+ str = g_match_info_fetch (info, ilac);
+ lac = parse_uint (str, 16, 1, 0xFFFF, &foo);
+ g_free (str);
+ }
+
+ /* Cell ID */
+ if (ici) {
+ str = g_match_info_fetch (info, ici);
+ ci = parse_uint (str, 16, 1, 0x0FFFFFFE, &foo);
+ g_free (str);
+ }
+
+ /* Access Technology */
+ if (iact) {
+ str = g_match_info_fetch (info, iact);
+ act = (gint) parse_uint (str, 10, 0, 7, &foo);
+ g_free (str);
+ if (!foo)
+ act = -1;
+ }
+
+ *out_reg_state = (guint32) stat;
+ if (stat != 4) {
+ /* Don't fill in lac/ci/act if the device's state is unknown */
+ *out_lac = lac;
+ *out_ci = ci;
+ *out_act = act;
+ }
+ return TRUE;
+}
+
+/*************************************************************************/
+
+gboolean
+mm_cdma_parse_spservice_response (const char *reply,
+ MMModemCdmaRegistrationState *out_cdma_1x_state,
+ MMModemCdmaRegistrationState *out_evdo_state)
+{
+ const char *p;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (out_cdma_1x_state != NULL, FALSE);
+ g_return_val_if_fail (out_evdo_state != NULL, FALSE);
+
+ p = mm_strip_tag (reply, "+SPSERVICE:");
+ if (!isdigit (*p))
+ return FALSE;
+
+ switch (atoi (p)) {
+ case 0: /* no service */
+ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ break;
+ case 1: /* 1xRTT */
+ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ break;
+ case 2: /* EVDO rev 0 */
+ case 3: /* EVDO rev A */
+ *out_cdma_1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ *out_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*************************************************************************/
+
+typedef struct {
+ int num;
+ gboolean roam_ind;
+ const char *banner;
+} EriItem;
+
+/* NOTE: these may be Sprint-specific for now... */
+static const EriItem eris[] = {
+ { 0, TRUE, "Digital or Analog Roaming" },
+ { 1, FALSE, "Home" },
+ { 2, TRUE, "Digital or Analog Roaming" },
+ { 3, TRUE, "Out of neighborhood" },
+ { 4, TRUE, "Out of building" },
+ { 5, TRUE, "Preferred system" },
+ { 6, TRUE, "Available System" },
+ { 7, TRUE, "Alliance Partner" },
+ { 8, TRUE, "Premium Partner" },
+ { 9, TRUE, "Full Service Functionality" },
+ { 10, TRUE, "Partial Service Functionality" },
+ { 64, TRUE, "Preferred system" },
+ { 65, TRUE, "Available System" },
+ { 66, TRUE, "Alliance Partner" },
+ { 67, TRUE, "Premium Partner" },
+ { 68, TRUE, "Full Service Functionality" },
+ { 69, TRUE, "Partial Service Functionality" },
+ { 70, TRUE, "Analog A" },
+ { 71, TRUE, "Analog B" },
+ { 72, TRUE, "CDMA 800 A" },
+ { 73, TRUE, "CDMA 800 B" },
+ { 74, TRUE, "International Roaming" },
+ { 75, TRUE, "Extended Network" },
+ { 76, FALSE, "Campus" },
+ { 77, FALSE, "In Building" },
+ { 78, TRUE, "Regional" },
+ { 79, TRUE, "Community" },
+ { 80, TRUE, "Business" },
+ { 81, TRUE, "Zone 1" },
+ { 82, TRUE, "Zone 2" },
+ { 83, TRUE, "National" },
+ { 84, TRUE, "Local" },
+ { 85, TRUE, "City" },
+ { 86, TRUE, "Government" },
+ { 87, TRUE, "USA" },
+ { 88, TRUE, "State" },
+ { 89, TRUE, "Resort" },
+ { 90, TRUE, "Headquarters" },
+ { 91, TRUE, "Personal" },
+ { 92, FALSE, "Home" },
+ { 93, TRUE, "Residential" },
+ { 94, TRUE, "University" },
+ { 95, TRUE, "College" },
+ { 96, TRUE, "Hotel Guest" },
+ { 97, TRUE, "Rental" },
+ { 98, FALSE, "Corporate" },
+ { 99, FALSE, "Home Provider" },
+ { 100, FALSE, "Campus" },
+ { 101, FALSE, "In Building" },
+ { 102, TRUE, "Regional" },
+ { 103, TRUE, "Community" },
+ { 104, TRUE, "Business" },
+ { 105, TRUE, "Zone 1" },
+ { 106, TRUE, "Zone 2" },
+ { 107, TRUE, "National" },
+ { 108, TRUE, "Local" },
+ { 109, TRUE, "City" },
+ { 110, TRUE, "Government" },
+ { 111, TRUE, "USA" },
+ { 112, TRUE, "State" },
+ { 113, TRUE, "Resort" },
+ { 114, TRUE, "Headquarters" },
+ { 115, TRUE, "Personal" },
+ { 116, FALSE, "Home" },
+ { 117, TRUE, "Residential" },
+ { 118, TRUE, "University" },
+ { 119, TRUE, "College" },
+ { 120, TRUE, "Hotel Guest" },
+ { 121, TRUE, "Rental" },
+ { 122, FALSE, "Corporate" },
+ { 123, FALSE, "Home Provider" },
+ { 124, TRUE, "International" },
+ { 125, TRUE, "International" },
+ { 126, TRUE, "International" },
+ { 127, FALSE, "Premium Service" },
+ { 128, FALSE, "Enhanced Service" },
+ { 129, FALSE, "Enhanced Digital" },
+ { 130, FALSE, "Enhanced Roaming" },
+ { 131, FALSE, "Alliance Service" },
+ { 132, FALSE, "Alliance Network" },
+ { 133, FALSE, "Data Roaming" }, /* Sprint: Vision Roaming */
+ { 134, FALSE, "Extended Service" },
+ { 135, FALSE, "Expanded Services" },
+ { 136, FALSE, "Expanded Network" },
+ { 137, TRUE, "Premium Service" },
+ { 138, TRUE, "Enhanced Service" },
+ { 139, TRUE, "Enhanced Digital" },
+ { 140, TRUE, "Enhanced Roaming" },
+ { 141, TRUE, "Alliance Service" },
+ { 142, TRUE, "Alliance Network" },
+ { 143, TRUE, "Data Roaming" }, /* Sprint: Vision Roaming */
+ { 144, TRUE, "Extended Service" },
+ { 145, TRUE, "Expanded Services" },
+ { 146, TRUE, "Expanded Network" },
+ { 147, TRUE, "Premium Service" },
+ { 148, TRUE, "Enhanced Service" },
+ { 149, TRUE, "Enhanced Digital" },
+ { 150, TRUE, "Enhanced Roaming" },
+ { 151, TRUE, "Alliance Service" },
+ { 152, TRUE, "Alliance Network" },
+ { 153, TRUE, "Data Roaming" }, /* Sprint: Vision Roaming */
+ { 154, TRUE, "Extended Service" },
+ { 155, TRUE, "Expanded Services" },
+ { 156, TRUE, "Expanded Network" },
+ { 157, TRUE, "Premium International" },
+ { 158, TRUE, "Premium International" },
+ { 159, TRUE, "Premium International" },
+ { 160, TRUE, NULL },
+ { 161, TRUE, NULL },
+ { 162, FALSE, NULL },
+ { 163, FALSE, NULL },
+ { 164, FALSE, "Extended Voice/Data Network" },
+ { 165, FALSE, "Extended Voice/Data Network" },
+ { 166, TRUE, "Extended Voice/Data Network" },
+ { 167, FALSE, "Extended Broadband" },
+ { 168, FALSE, "Extended Broadband" },
+ { 169, TRUE, "Extended Broadband" },
+ { 170, FALSE, "Extended Data" },
+ { 171, FALSE, "Extended Data" },
+ { 172, TRUE, "Extended Data" },
+ { 173, FALSE, "Extended Data Network" },
+ { 174, FALSE, "Extended Data Network" },
+ { 175, TRUE, "Extended Data Network" },
+ { 176, FALSE, "Extended Network" },
+ { 177, FALSE, "Extended Network" },
+ { 178, TRUE, "Extended Network" },
+ { 179, FALSE, "Extended Service" },
+ { 180, TRUE, "Extended Service" },
+ { 181, FALSE, "Extended Voice" },
+ { 182, FALSE, "Extended Voice" },
+ { 183, TRUE, "Extended Voice" },
+ { 184, FALSE, "Extended Voice/Data" },
+ { 185, FALSE, "Extended Voice/Data" },
+ { 186, TRUE, "Extended Voice/Data" },
+ { 187, FALSE, "Extended Voice Network" },
+ { 188, FALSE, "Extended Voice Network" },
+ { 189, TRUE, "Extended Voice Network" },
+ { 190, FALSE, "Extended Voice/Data" },
+ { 191, FALSE, "Extended Voice/Data" },
+ { 192, TRUE, "Extended Voice/Data" },
+ { 193, TRUE, "International" },
+ { 194, FALSE, "International Services" },
+ { 195, FALSE, "International Voice" },
+ { 196, FALSE, "International Voice/Data" },
+ { 197, FALSE, "International Voice/Data" },
+ { 198, TRUE, "International Voice/Data" },
+ { 199, FALSE, "Extended Voice/Data Network" },
+ { 200, TRUE, "Extended Voice/Data Network" },
+ { 201, TRUE, "Extended Voice/Data Network" },
+ { 202, FALSE, "Extended Broadband" },
+ { 203, TRUE, "Extended Broadband" },
+ { 204, TRUE, "Extended Broadband" },
+ { 205, FALSE, "Extended Data" },
+ { 206, TRUE, "Extended Data" },
+ { 207, TRUE, "Extended Data" },
+ { 208, FALSE, "Extended Data Network" },
+ { 209, TRUE, "Extended Data Network" },
+ { 210, TRUE, "Extended Data Network" },
+ { 211, FALSE, "Extended Network" },
+ { 212, TRUE, "Extended Network" },
+ { 213, FALSE, "Extended Service" },
+ { 214, TRUE, "Extended Service" },
+ { 215, TRUE, "Extended Service" },
+ { 216, FALSE, "Extended Voice" },
+ { 217, TRUE, "Extended Voice" },
+ { 218, TRUE, "Extended Voice" },
+ { 219, FALSE, "Extended Voice/Data" },
+ { 220, TRUE, "Extended Voice/Data" },
+ { 221, TRUE, "Extended Voice/Data" },
+ { 222, FALSE, "Extended Voice Network" },
+ { 223, FALSE, "Extended Voice Network" },
+ { 224, TRUE, "Extended Voice Network" },
+ { 225, FALSE, "Extended Voice/Data" },
+ { 226, TRUE, "Extended Voice/Data" },
+ { 227, TRUE, "Extended Voice/Data" },
+ { 228, TRUE, "International" },
+ { 229, TRUE, "International" },
+ { 230, TRUE, "International Services" },
+ { 231, TRUE, "International Voice" },
+ { 232, FALSE, "International Voice/Data" },
+ { 233, TRUE, "International Voice/Data" },
+ { 234, TRUE, "International Voice/Data" },
+ { 235, TRUE, "Premium International" },
+ { 236, TRUE, NULL },
+ { 237, TRUE, NULL },
+ { 238, FALSE, NULL },
+ { 239, FALSE, NULL },
+ { -1, FALSE, NULL },
+};
+
+gboolean
+mm_cdma_parse_eri (const char *reply,
+ gboolean *out_roaming,
+ guint32 *out_ind,
+ const char **out_desc)
+{
+ long int ind;
+ const EriItem *iter = &eris[0];
+ gboolean found = FALSE;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (out_roaming != NULL, FALSE);
+
+ errno = 0;
+ ind = strtol (reply, NULL, 10);
+ if (errno == 0) {
+ if (out_ind)
+ *out_ind = ind;
+
+ while (iter->num != -1) {
+ if (iter->num == ind) {
+ *out_roaming = iter->roam_ind;
+ if (out_desc)
+ *out_desc = iter->banner;
+ found = TRUE;
+ break;
+ }
+ iter++;
+ }
+ }
+
+ return found;
+}
+
+/*************************************************************************/
+
+gboolean
+mm_gsm_parse_cscs_support_response (const char *reply,
+ MMModemCharset *out_charsets)
+{
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ GRegex *r;
+ GMatchInfo *match_info;
+ char *p, *str;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (out_charsets != NULL, FALSE);
+
+ /* Find the first '(' or '"'; the general format is:
+ *
+ * +CSCS: ("IRA","GSM","UCS2")
+ *
+ * but some devices (some Blackberries) don't include the ().
+ */
+ p = strchr (reply, '(');
+ if (p)
+ p++;
+ else {
+ p = strchr (reply, '"');
+ if (!p)
+ return FALSE;
+ }
+
+ /* Now parse each charset */
+ r = g_regex_new ("\\s*([^,\\)]+)\\s*", 0, 0, NULL);
+ if (!r)
+ return FALSE;
+
+ if (g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) {
+ while (g_match_info_matches (match_info)) {
+ str = g_match_info_fetch (match_info, 1);
+ charsets |= mm_modem_charset_from_string (str);
+ g_free (str);
+
+ g_match_info_next (match_info, NULL);
+ success = TRUE;
+ }
+ g_match_info_free (match_info);
+ }
+ g_regex_unref (r);
+
+ if (success)
+ *out_charsets = charsets;
+
+ return success;
+}
+
+/*************************************************************************/
+
+MMModemGsmAccessTech
+mm_gsm_string_to_access_tech (const char *string)
+{
+ g_return_val_if_fail (string != NULL, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+
+ /* Better technologies are listed first since modems sometimes say
+ * stuff like "GPRS/EDGE" and that should be handled as EDGE.
+ */
+ if (strcasestr (string, "HSPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ else if (strcasestr (string, "HSDPA/HSUPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSPA;
+ else if (strcasestr (string, "HSUPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+ else if (strcasestr (string, "HSDPA"))
+ return MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+ else if (strcasestr (string, "UMTS"))
+ return MM_MODEM_GSM_ACCESS_TECH_UMTS;
+ else if (strcasestr (string, "EDGE"))
+ return MM_MODEM_GSM_ACCESS_TECH_EDGE;
+ else if (strcasestr (string, "GPRS"))
+ return MM_MODEM_GSM_ACCESS_TECH_GPRS;
+ else if (strcasestr (string, "GSM"))
+ return MM_MODEM_GSM_ACCESS_TECH_GSM;
+
+ return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+}
+
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index ddc9cbc..fb100bc 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -17,6 +17,10 @@
#ifndef MM_MODEM_HELPERS_H
#define MM_MODEM_HELPERS_H
+#include "mm-modem-cdma.h"
+#include "mm-modem-gsm.h"
+#include "mm-charsets.h"
+
#define MM_SCAN_TAG_STATUS "status"
#define MM_SCAN_TAG_OPER_LONG "operator-long"
#define MM_SCAN_TAG_OPER_SHORT "operator-short"
@@ -27,5 +31,33 @@ GPtrArray *mm_gsm_parse_scan_response (const char *reply, GError **error);
void mm_gsm_destroy_scan_data (gpointer data);
+GPtrArray *mm_gsm_creg_regex_get (gboolean solicited);
+
+void mm_gsm_creg_regex_destroy (GPtrArray *array);
+
+gboolean mm_gsm_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_cgreg,
+ GError **error);
+
+const char *mm_strip_tag (const char *str, const char *cmd);
+
+gboolean mm_cdma_parse_spservice_response (const char *reply,
+ MMModemCdmaRegistrationState *out_cdma_1x_state,
+ MMModemCdmaRegistrationState *out_evdo_state);
+
+gboolean mm_cdma_parse_eri (const char *reply,
+ gboolean *out_roaming,
+ guint32 *out_ind,
+ const char **out_desc);
+
+gboolean mm_gsm_parse_cscs_support_response (const char *reply,
+ MMModemCharset *out_charsets);
+
+MMModemGsmAccessTech mm_gsm_string_to_access_tech (const char *string);
+
#endif /* MM_MODEM_HELPERS_H */
diff --git a/src/mm-modem-location.c b/src/mm-modem-location.c
new file mode 100644
index 0000000..0018295
--- /dev/null
+++ b/src/mm-modem-location.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+
+#include "mm-modem-location.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-marshal.h"
+
+static void impl_modem_location_enable (MMModemLocation *modem,
+ gboolean enable,
+ gboolean signal_location,
+ DBusGMethodInvocation *context);
+
+static void impl_modem_location_get_location (MMModemLocation *modem,
+ DBusGMethodInvocation *context);
+
+#include "mm-modem-location-glue.h"
+
+/*****************************************************************************/
+
+static void
+async_call_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+async_call_not_supported (MMModemLocation *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ gboolean enable;
+ gboolean signals_location;
+} LocAuthInfo;
+
+static void
+loc_auth_info_destroy (gpointer data)
+{
+ LocAuthInfo *info = data;
+
+ memset (info, 0, sizeof (LocAuthInfo));
+ g_free (info);
+}
+
+static LocAuthInfo *
+loc_auth_info_new (gboolean enable, gboolean signals_location)
+{
+ LocAuthInfo *info;
+
+ info = g_malloc0 (sizeof (LocAuthInfo));
+ info->enable = enable;
+ info->signals_location = signals_location;
+ return info;
+}
+
+/*****************************************************************************/
+
+void
+mm_modem_location_enable (MMModemLocation *self,
+ gboolean enable,
+ gboolean signals_location,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LOCATION (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LOCATION_GET_INTERFACE (self)->enable)
+ MM_MODEM_LOCATION_GET_INTERFACE (self)->enable (self, enable, signals_location, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+loc_enable_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (owner);
+ LocAuthInfo *info = (LocAuthInfo *) user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise enable location gathering */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_location_enable (self, info->enable, info->signals_location, async_call_done, context);
+}
+
+static void
+impl_modem_location_enable (MMModemLocation *modem,
+ gboolean enable,
+ gboolean signals_location,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ LocAuthInfo *info;
+
+ info = loc_auth_info_new (enable, signals_location);
+
+ /* Make sure the caller is authorized to enable location gathering */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_LOCATION,
+ context,
+ loc_enable_auth_cb,
+ info,
+ loc_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+loc_get_invoke (MMCallbackInfo *info)
+{
+ MMModemLocationGetFn callback = (MMModemLocationGetFn) info->callback;
+
+ callback ((MMModemLocation *) info->modem, NULL, info->error, info->user_data);
+}
+
+static void
+async_get_call_not_supported (MMModemLocation *self,
+ MMModemLocationGetFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new_full (MM_MODEM (self),
+ loc_get_invoke,
+ G_CALLBACK (callback),
+ user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+void
+mm_modem_location_get_location (MMModemLocation *self,
+ MMModemLocationGetFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LOCATION (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_LOCATION_GET_INTERFACE (self)->get_location)
+ MM_MODEM_LOCATION_GET_INTERFACE (self)->get_location (self, callback, user_data);
+ else
+ async_get_call_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+async_get_call_done (MMModemLocation *self,
+ GHashTable *locations,
+ GError *error,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, locations);
+}
+
+static void
+loc_get_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise get the location */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_location_get_location (self, async_get_call_done, context);
+}
+
+static void
+impl_modem_location_get_location (MMModemLocation *modem,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ LocAuthInfo *info;
+
+ info = loc_auth_info_new (FALSE, FALSE);
+
+ /* Make sure the caller is authorized to enable location gathering */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_LOCATION,
+ context,
+ loc_get_auth_cb,
+ info,
+ loc_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_location_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boxed (MM_MODEM_LOCATION_LOCATION,
+ "Location",
+ "Available location information",
+ G_TYPE_HASH_TABLE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_LOCATION_CAPABILITIES,
+ "Capabilities",
+ "Supported location information methods",
+ MM_MODEM_LOCATION_CAPABILITY_UNKNOWN,
+ MM_MODEM_LOCATION_CAPABILITY_GPS_NMEA
+ | MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI,
+ MM_MODEM_LOCATION_CAPABILITY_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_MODEM_LOCATION_ENABLED,
+ "Enabled",
+ "Whether or not location gathering is enabled",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_MODEM_LOCATION_SIGNALS_LOCATION,
+ "SignalsLocation",
+ "Whether or not location updates are emitted as signals",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ initialized = TRUE;
+}
+
+GType
+mm_modem_location_get_type (void)
+{
+ static GType loc_type = 0;
+
+ if (!G_UNLIKELY (loc_type)) {
+ const GTypeInfo loc_info = {
+ sizeof (MMModemLocation), /* class_size */
+ mm_modem_location_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ loc_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMModemLocation",
+ &loc_info, 0);
+
+ g_type_interface_add_prerequisite (loc_type, G_TYPE_OBJECT);
+ dbus_g_object_type_install_info (loc_type, &dbus_glib_mm_modem_location_object_info);
+
+ /* Register some shadow properties to handle Enabled and Capabilities
+ * since these could be used by other interfaces.
+ */
+ dbus_g_object_type_register_shadow_property (loc_type,
+ "Enabled",
+ MM_MODEM_LOCATION_ENABLED);
+ dbus_g_object_type_register_shadow_property (loc_type,
+ "Capabilities",
+ MM_MODEM_LOCATION_CAPABILITIES);
+ }
+
+ return loc_type;
+}
diff --git a/src/mm-modem-location.h b/src/mm-modem-location.h
new file mode 100644
index 0000000..dd622f1
--- /dev/null
+++ b/src/mm-modem-location.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_MODEM_LOCATION_H
+#define MM_MODEM_LOCATION_H
+
+#include <mm-modem.h>
+
+#define MM_TYPE_MODEM_LOCATION (mm_modem_location_get_type ())
+#define MM_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LOCATION, MMModemLocation))
+#define MM_IS_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LOCATION))
+#define MM_MODEM_LOCATION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_LOCATION, MMModemLocation))
+
+#define MM_MODEM_LOCATION_CAPABILITIES "location-capabilities"
+#define MM_MODEM_LOCATION_ENABLED "location-enabled"
+#define MM_MODEM_LOCATION_SIGNALS_LOCATION "signals-location"
+#define MM_MODEM_LOCATION_LOCATION "location"
+
+typedef enum {
+ MM_MODEM_LOCATION_CAPABILITY_UNKNOWN = 0x00000000,
+ MM_MODEM_LOCATION_CAPABILITY_GPS_NMEA = 0x00000001,
+ MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI = 0x00000002,
+
+ MM_MODEM_LOCATION_CAPABILITY_LAST = MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI
+} MMModemLocationCapabilities;
+
+typedef struct _MMModemLocation MMModemLocation;
+
+typedef void (*MMModemLocationGetFn) (MMModemLocation *modem,
+ GHashTable *locations,
+ GError *error,
+ gpointer user_data);
+
+struct _MMModemLocation {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ void (*enable) (MMModemLocation *modem,
+ gboolean enable,
+ gboolean signal_location,
+ MMModemFn callback,
+ gpointer user_data);
+
+ void (*get_location) (MMModemLocation *modem,
+ MMModemLocationGetFn callback,
+ gpointer user_data);
+};
+
+GType mm_modem_location_get_type (void);
+
+void mm_modem_location_enable (MMModemLocation *self,
+ gboolean enable,
+ gboolean signal_location,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_location_get_location (MMModemLocation *self,
+ MMModemLocationGetFn callback,
+ gpointer user_data);
+
+#endif /* MM_MODEM_LOCATION_H */
diff --git a/src/mm-modem.c b/src/mm-modem.c
index a65d883..221c9ea 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -14,9 +14,11 @@
* Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include "mm-modem.h"
+#include "mm-options.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
#include "mm-marshal.h"
@@ -26,6 +28,7 @@ static void impl_modem_connect (MMModem *modem, const char *number, DBusGMethodI
static void impl_modem_disconnect (MMModem *modem, DBusGMethodInvocation *context);
static void impl_modem_get_ip4_config (MMModem *modem, DBusGMethodInvocation *context);
static void impl_modem_get_info (MMModem *modem, DBusGMethodInvocation *context);
+static void impl_modem_factory_reset (MMModem *modem, const char *code, DBusGMethodInvocation *context);
#include "mm-modem-glue.h"
@@ -148,12 +151,15 @@ disable_disconnect_done (MMModem *self,
}
if (error) {
+ char *device = mm_modem_get_device (self);
+
/* Don't really care what the error was; log it and proceed to disable */
g_warning ("%s: (%s): error disconnecting the modem while disabling: (%d) %s",
__func__,
- mm_modem_get_device (self),
+ device,
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
+ g_free (device);
}
finish_disable (self, cb_data->callback, cb_data->user_data);
g_free (cb_data);
@@ -471,6 +477,106 @@ impl_modem_get_info (MMModem *modem,
/*****************************************************************************/
+static void
+factory_reset_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModem *self = MM_MODEM (owner);
+ const char *code = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise try to reset the modem */
+ if (!mm_modem_auth_finish (self, req, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ } else
+ mm_modem_factory_reset (self, code, async_call_done, context);
+}
+
+static void
+impl_modem_factory_reset (MMModem *modem,
+ const char *code,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+
+ /* Make sure the caller is authorized to reset the device */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ context,
+ factory_reset_auth_cb,
+ g_strdup (code),
+ g_free,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+void
+mm_modem_factory_reset (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (code != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->factory_reset)
+ MM_MODEM_GET_INTERFACE (self)->factory_reset (self, code, callback, user_data);
+ else
+ async_op_not_supported (self, callback, user_data);
+}
+
+/*****************************************************************************/
+
+void
+mm_modem_get_supported_charsets (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->get_supported_charsets)
+ MM_MODEM_GET_INTERFACE (self)->get_supported_charsets (self, callback, user_data);
+ else {
+ info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+ }
+}
+
+void
+mm_modem_set_charset (MMModem *self,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ g_return_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN);
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GET_INTERFACE (self)->set_charset)
+ MM_MODEM_GET_INTERFACE (self)->set_charset (self, charset, callback, user_data);
+ else {
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+ }
+}
+
+/*****************************************************************************/
+
gboolean
mm_modem_owns_port (MMModem *self,
const char *subsys,
@@ -598,16 +704,76 @@ mm_modem_set_state (MMModem *self,
dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG);
if (dbus_path) {
- g_message ("Modem %s: state changed (%s -> %s)",
- dbus_path,
- state_to_string (old_state),
- state_to_string (new_state));
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> Modem %s: state changed (%s -> %s)",
+ tv.tv_sec,
+ tv.tv_usec,
+ dbus_path,
+ state_to_string (old_state),
+ state_to_string (new_state));
+ } else {
+ g_message ("Modem %s: state changed (%s -> %s)",
+ dbus_path,
+ state_to_string (old_state),
+ state_to_string (new_state));
+ }
}
}
}
/*****************************************************************************/
+gboolean
+mm_modem_auth_request (MMModem *self,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (authorization != NULL, FALSE);
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ g_return_val_if_fail (MM_MODEM_GET_INTERFACE (self)->auth_request, FALSE);
+ return MM_MODEM_GET_INTERFACE (self)->auth_request (self,
+ authorization,
+ context,
+ callback,
+ callback_data,
+ notify,
+ error);
+}
+
+gboolean
+mm_modem_auth_finish (MMModem *self,
+ MMAuthRequest *req,
+ GError **error)
+{
+ gboolean success;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (req != NULL, FALSE);
+
+ g_return_val_if_fail (MM_MODEM_GET_INTERFACE (self)->auth_finish, FALSE);
+ success = MM_MODEM_GET_INTERFACE (self)->auth_finish (self, req, error);
+
+ /* If the request failed, the implementor *should* return an error */
+ if (!success && error)
+ g_warn_if_fail (*error != NULL);
+
+ return success;
+}
+
+/*****************************************************************************/
+
static void
mm_modem_init (gpointer g_iface)
{
@@ -694,6 +860,31 @@ mm_modem_init (gpointer g_iface)
FALSE,
G_PARAM_READABLE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_EQUIPMENT_IDENTIFIER,
+ "EquipmentIdentifier",
+ "The equipment identifier of the device",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_UNLOCK_REQUIRED,
+ "UnlockRequired",
+ "Whether or not the modem requires an unlock "
+ "code to become usable, and if so, which unlock code is required",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_UNLOCK_RETRIES,
+ "UnlockRetries",
+ "The remaining number of unlock attempts",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE));
+
/* Signals */
g_signal_new ("state-changed",
iface_type,
diff --git a/src/mm-modem.h b/src/mm-modem.h
index e8dd7ea..0915180 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -18,8 +18,11 @@
#define MM_MODEM_H
#include <glib-object.h>
+#include <dbus/dbus-glib-lowlevel.h>
#include "mm-port.h"
+#include "mm-auth-provider.h"
+#include "mm-charsets.h"
typedef enum {
MM_MODEM_STATE_UNKNOWN = 0,
@@ -55,6 +58,9 @@ typedef enum {
#define MM_MODEM_TYPE "type"
#define MM_MODEM_IP_METHOD "ip-method"
#define MM_MODEM_ENABLED "enabled"
+#define MM_MODEM_EQUIPMENT_IDENTIFIER "equipment-identifier"
+#define MM_MODEM_UNLOCK_REQUIRED "unlock-required"
+#define MM_MODEM_UNLOCK_RETRIES "unlock-retries"
#define MM_MODEM_VALID "valid" /* not exported */
#define MM_MODEM_PLUGIN "plugin" /* not exported */
#define MM_MODEM_STATE "state" /* not exported */
@@ -78,7 +84,10 @@ typedef enum {
MM_MODEM_PROP_VALID, /* Not exported */
MM_MODEM_PROP_PLUGIN, /* Not exported */
MM_MODEM_PROP_STATE, /* Not exported */
- MM_MODEM_PROP_ENABLED
+ MM_MODEM_PROP_ENABLED,
+ MM_MODEM_PROP_EQUIPMENT_IDENTIFIER,
+ MM_MODEM_PROP_UNLOCK_REQUIRED,
+ MM_MODEM_PROP_UNLOCK_RETRIES
} MMModemProp;
typedef struct _MMModem MMModem;
@@ -154,6 +163,36 @@ struct _MMModem {
MMModemInfoFn callback,
gpointer user_data);
+ void (*get_supported_charsets) (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ void (*set_charset) (MMModem *self,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data);
+
+
+ /* Normally implemented by the modem base class; plugins should
+ * never need to implement this.
+ */
+ gboolean (*auth_request) (MMModem *self,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error);
+
+ gboolean (*auth_finish) (MMModem *self,
+ MMAuthRequest *req,
+ GError **error);
+
+ void (*factory_reset) (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data);
+
/* Signals */
void (*state_changed) (MMModem *self,
MMModemState new_state,
@@ -203,6 +242,20 @@ void mm_modem_get_info (MMModem *self,
MMModemInfoFn callback,
gpointer user_data);
+void mm_modem_get_supported_charsets (MMModem *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+void mm_modem_set_charset (MMModem *self,
+ MMModemCharset charset,
+ MMModemFn callback,
+ gpointer user_data);
+
+void mm_modem_factory_reset (MMModem *self,
+ const char *code,
+ MMModemFn callback,
+ gpointer user_data);
+
gboolean mm_modem_get_valid (MMModem *self);
char *mm_modem_get_device (MMModem *self);
@@ -215,5 +268,21 @@ void mm_modem_set_state (MMModem *self,
GError *mm_modem_check_removed (MMModem *self, const GError *error);
+/* Request authorization to perform an action. Used by D-Bus method
+ * handlers to ensure that the incoming request is authorized to perform
+ * the action it's requesting.
+ */
+gboolean mm_modem_auth_request (MMModem *self,
+ const char *authorization,
+ DBusGMethodInvocation *context,
+ MMAuthRequestCb callback,
+ gpointer callback_data,
+ GDestroyNotify notify,
+ GError **error);
+
+gboolean mm_modem_auth_finish (MMModem *self,
+ MMAuthRequest *req,
+ GError **error);
+
#endif /* MM_MODEM_H */
diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c
index 202eaac..80d0f90 100644
--- a/src/mm-plugin-base.c
+++ b/src/mm-plugin-base.c
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#define _GNU_SOURCE /* for strcasestr */
@@ -26,10 +26,14 @@
#include <gudev/gudev.h>
#include "mm-plugin-base.h"
-#include "mm-serial-port.h"
+#include "mm-at-serial-port.h"
+#include "mm-qcdm-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-errors.h"
#include "mm-marshal.h"
+#include "mm-utils.h"
+#include "libqcdm/src/commands.h"
+#include "libqcdm/src/utils.h"
static void plugin_init (MMPlugin *plugin_class);
@@ -48,8 +52,6 @@ static GHashTable *cached_caps = NULL;
typedef struct {
char *name;
GUdevClient *client;
-
- GHashTable *modems;
GHashTable *tasks;
} MMPluginBasePrivate;
@@ -77,6 +79,8 @@ typedef enum {
PROBE_STATE_LAST
} ProbeState;
+static void probe_complete (MMPluginBaseSupportsTask *task);
+
/*****************************************************************************/
G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OBJECT)
@@ -86,13 +90,15 @@ G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OB
typedef struct {
MMPluginBase *plugin;
GUdevDevice *port;
- GUdevDevice *physdev;
+ char *physdev_path;
char *driver;
guint open_id;
guint32 open_tries;
+ guint full_id;
- MMSerialPort *probe_port;
+ MMAtSerialPort *probe_port;
+ MMQcdmSerialPort *qcdm_port;
guint32 probed_caps;
ProbeState probe_state;
guint probe_id;
@@ -112,7 +118,7 @@ typedef struct {
static MMPluginBaseSupportsTask *
supports_task_new (MMPluginBase *self,
GUdevDevice *port,
- GUdevDevice *physdev,
+ const char *physdev_path,
const char *driver,
MMSupportsPortResultFunc callback,
gpointer callback_data)
@@ -123,7 +129,7 @@ supports_task_new (MMPluginBase *self,
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL);
g_return_val_if_fail (port != NULL, NULL);
- g_return_val_if_fail (physdev != NULL, NULL);
+ g_return_val_if_fail (physdev_path != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (callback != NULL, NULL);
@@ -132,7 +138,7 @@ supports_task_new (MMPluginBase *self,
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
priv->plugin = self;
priv->port = g_object_ref (port);
- priv->physdev = g_object_ref (physdev);
+ priv->physdev_path = g_strdup (physdev_path);
priv->driver = g_strdup (driver);
priv->callback = callback;
priv->callback_data = callback_data;
@@ -158,13 +164,13 @@ mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task)
return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->port;
}
-GUdevDevice *
-mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task)
+const char *
+mm_plugin_base_supports_task_get_physdev_path (MMPluginBaseSupportsTask *task)
{
g_return_val_if_fail (task != NULL, NULL);
g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL);
- return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->physdev;
+ return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->physdev_path;
}
const char *
@@ -198,6 +204,11 @@ mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task,
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
g_return_if_fail (priv->callback != NULL);
+ if (priv->full_id) {
+ g_source_remove (priv->full_id);
+ priv->full_id = 0;
+ }
+
subsys = g_udev_device_get_subsystem (priv->port);
name = g_udev_device_get_name (priv->port);
@@ -239,11 +250,11 @@ supports_task_dispose (GObject *object)
{
MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object);
- if (MM_IS_SERIAL_PORT (priv->port))
- mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->port));
+ if (MM_IS_SERIAL_PORT (priv->probe_port))
+ mm_serial_port_flash_cancel (MM_SERIAL_PORT (priv->probe_port));
g_object_unref (priv->port);
- g_object_unref (priv->physdev);
+ g_free (priv->physdev_path);
g_free (priv->driver);
g_free (priv->probe_resp);
g_clear_error (&(priv->probe_error));
@@ -251,11 +262,19 @@ supports_task_dispose (GObject *object)
if (priv->open_id)
g_source_remove (priv->open_id);
+ if (priv->full_id)
+ g_source_remove (priv->full_id);
if (priv->probe_id)
g_source_remove (priv->probe_id);
- if (priv->probe_port)
+ if (priv->probe_port) {
+ mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
g_object_unref (priv->probe_port);
+ }
+ if (priv->qcdm_port) {
+ mm_serial_port_close (MM_SERIAL_PORT (priv->qcdm_port));
+ g_object_unref (priv->qcdm_port);
+ }
G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object);
}
@@ -334,7 +353,8 @@ parse_cpin (const char *buf)
|| strcasestr (buf, "PH-SP PIN")
|| strcasestr (buf, "PH-SP PUK")
|| strcasestr (buf, "PH-CORP PIN")
- || strcasestr (buf, "PH-CORP PUK"))
+ || strcasestr (buf, "PH-CORP PUK")
+ || strcasestr (buf, "READY"))
return MM_PLUGIN_BASE_PORT_CAP_GSM;
return 0;
@@ -349,6 +369,49 @@ parse_cgmm (const char *buf)
return 0;
}
+static const char *dq_strings[] = {
+ /* Option Icera-based devices */
+ "option/faema_",
+ "os_logids.h",
+ /* Sierra CnS port */
+ "NETWORK SERVICE CHANGE",
+ NULL
+};
+
+static void
+port_buffer_full (MMSerialPort *port, GByteArray *buffer, gpointer user_data)
+{
+ MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (user_data);
+ const char **iter;
+ size_t iter_len;
+ int i;
+
+ /* Check for an immediate disqualification response. There are some
+ * ports (Option Icera-based chipsets have them, as do Qualcomm Gobi
+ * devices before their firmware is loaded) that just shouldn't be
+ * probed if we get a certain response because we know they can't be
+ * used. Kernel bugs (at least with 2.6.31 and 2.6.32) also trigger port
+ * flow control kernel oopses if we read too much data for these ports.
+ */
+
+ for (iter = &dq_strings[0]; iter && *iter; iter++) {
+ /* Search in the response for the item; the response could have embedded
+ * nulls so we can't use memcmp() or strstr() on the whole response.
+ */
+ iter_len = strlen (*iter);
+ for (i = 0; i < buffer->len - iter_len; i++) {
+ if (!memcmp (&buffer->data[i], *iter, iter_len)) {
+ /* Immediately close the port and complete probing */
+ priv->probed_caps = 0;
+ mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
+ probe_complete (task);
+ return;
+ }
+ }
+ }
+}
+
static gboolean
emit_probe_result (gpointer user_data)
{
@@ -356,9 +419,15 @@ emit_probe_result (gpointer user_data)
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
MMPlugin *self = mm_plugin_base_supports_task_get_plugin (task);
- /* Close the serial port */
- g_object_unref (task_priv->probe_port);
- task_priv->probe_port = NULL;
+ /* Close the serial ports */
+ if (task_priv->probe_port) {
+ g_object_unref (task_priv->probe_port);
+ task_priv->probe_port = NULL;
+ }
+ if (task_priv->qcdm_port) {
+ g_object_unref (task_priv->qcdm_port);
+ task_priv->qcdm_port = NULL;
+ }
task_priv->probe_id = 0;
g_signal_emit (self, signals[PROBE_RESULT], 0, task, task_priv->probed_caps);
@@ -368,17 +437,122 @@ emit_probe_result (gpointer user_data)
static void
probe_complete (MMPluginBaseSupportsTask *task)
{
- MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ MMPort *port;
+ port = priv->probe_port ? MM_PORT (priv->probe_port) : MM_PORT (priv->qcdm_port);
+ g_assert (port);
g_hash_table_insert (cached_caps,
- g_strdup (mm_port_get_device (MM_PORT (task_priv->probe_port))),
- GUINT_TO_POINTER (task_priv->probed_caps));
+ g_strdup (mm_port_get_device (port)),
+ GUINT_TO_POINTER (priv->probed_caps));
+
+ priv->probe_id = g_idle_add (emit_probe_result, task);
+}
+
+static void
+qcdm_verinfo_cb (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMPluginBaseSupportsTask *task;
+ MMPluginBaseSupportsTaskPrivate *priv;
+ QCDMResult *result;
+ GError *dm_error = NULL;
+
+ /* Just the initial poke; ignore it */
+ if (!user_data)
+ return;
+
+ task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data);
+ priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+
+ if (error) {
+ /* Probably not a QCDM port */
+ goto done;
+ }
+
+ /* Parse the response */
+ result = qcdm_cmd_version_info_result ((const char *) response->data, response->len, &dm_error);
+ if (!result) {
+ g_warning ("(%s) failed to parse QCDM version info command result: (%d) %s.",
+ g_udev_device_get_name (priv->port),
+ dm_error ? dm_error->code : -1,
+ dm_error && dm_error->message ? dm_error->message : "(unknown)");
+ g_clear_error (&dm_error);
+ goto done;
+ }
+
+ /* yay, probably a QCDM port */
+ qcdm_result_unref (result);
+ priv->probed_caps |= MM_PLUGIN_BASE_PORT_CAP_QCDM;
- task_priv->probe_id = g_idle_add (emit_probe_result, task);
+done:
+ probe_complete (task);
}
static void
-parse_response (MMSerialPort *port,
+try_qcdm_probe (MMPluginBaseSupportsTask *task)
+{
+ MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
+ const char *name;
+ GError *error = NULL;
+ GByteArray *verinfo = NULL, *verinfo2;
+ gint len;
+
+ /* Close the AT port */
+ if (priv->probe_port) {
+ mm_serial_port_close (MM_SERIAL_PORT (priv->probe_port));
+ g_object_unref (priv->probe_port);
+ priv->probe_port = NULL;
+ }
+
+ /* Open the QCDM port */
+ name = g_udev_device_get_name (priv->port);
+ g_assert (name);
+ priv->qcdm_port = mm_qcdm_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ if (priv->qcdm_port == NULL) {
+ g_warning ("(%s) failed to create new QCDM serial port.", name);
+ probe_complete (task);
+ return;
+ }
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->qcdm_port), &error)) {
+ g_warning ("(%s) failed to open new QCDM serial port: (%d) %s.",
+ name,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ probe_complete (task);
+ return;
+ }
+
+ /* Build up the probe command */
+ verinfo = g_byte_array_sized_new (50);
+ len = qcdm_cmd_version_info_new ((char *) verinfo->data, 50, &error);
+ if (len <= 0) {
+ g_byte_array_free (verinfo, TRUE);
+ g_warning ("(%s) failed to create QCDM version info command: (%d) %s.",
+ name,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ probe_complete (task);
+ return;
+ }
+ verinfo->len = len;
+
+ /* Queuing the command takes ownership over it; copy it for the second try */
+ verinfo2 = g_byte_array_sized_new (verinfo->len);
+ g_byte_array_append (verinfo2, verinfo->data, verinfo->len);
+
+ /* Send the command twice; the ports often need to be woken up */
+ mm_qcdm_serial_port_queue_command (priv->qcdm_port, verinfo, 3, qcdm_verinfo_cb, NULL);
+ mm_qcdm_serial_port_queue_command (priv->qcdm_port, verinfo2, 3, qcdm_verinfo_cb, task);
+}
+
+static void
+parse_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data);
@@ -391,7 +565,7 @@ real_handle_probe_response (MMPluginBase *self,
const GError *error)
{
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
- MMSerialPort *port = task_priv->probe_port;
+ MMAtSerialPort *port = task_priv->probe_port;
gboolean ignore_error = FALSE;
/* Some modems (Huawei E160g) won't respond to +GCAP with no SIM, but
@@ -401,16 +575,16 @@ real_handle_probe_response (MMPluginBase *self,
ignore_error = TRUE;
if (error && !ignore_error) {
- if (error->code == MM_SERIAL_RESPONSE_TIMEOUT) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
/* Try GCAP again */
if (task_priv->probe_state < PROBE_STATE_GCAP_TRY3) {
task_priv->probe_state++;
- mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
} else {
/* Otherwise, if all the GCAP tries timed out, ignore the port
- * as it's probably not an AT-capable port.
+ * as it's probably not an AT-capable port. Try QCDM.
*/
- probe_complete (task);
+ try_qcdm_probe (task);
}
return;
}
@@ -459,19 +633,19 @@ real_handle_probe_response (MMPluginBase *self,
switch (task_priv->probe_state) {
case PROBE_STATE_GCAP_TRY2:
case PROBE_STATE_GCAP_TRY3:
- mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, task);
break;
case PROBE_STATE_ATI:
/* After the last GCAP attempt, try ATI */
- mm_serial_port_queue_command (port, "I", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "I", 3, parse_response, task);
break;
case PROBE_STATE_CPIN:
/* After the ATI attempt, try CPIN */
- mm_serial_port_queue_command (port, "+CPIN?", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+CPIN?", 3, parse_response, task);
break;
case PROBE_STATE_CGMM:
/* After the CPIN attempt, try CGMM */
- mm_serial_port_queue_command (port, "+CGMM", 3, parse_response, task);
+ mm_at_serial_port_queue_command (port, "+CGMM", 3, parse_response, task);
break;
default:
/* Probably not GSM or CDMA */
@@ -515,7 +689,7 @@ handle_probe_response (gpointer user_data)
}
static void
-parse_response (MMSerialPort *port,
+parse_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -545,7 +719,7 @@ parse_response (MMSerialPort *port,
static void flash_done (MMSerialPort *port, GError *error, gpointer user_data);
static void
-custom_init_response (MMSerialPort *port,
+custom_init_response (MMAtSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -557,11 +731,11 @@ custom_init_response (MMSerialPort *port,
task_priv->custom_init_tries++;
if (task_priv->custom_init_tries < task_priv->custom_init_max_tries) {
/* Try the custom command again */
- flash_done (port, NULL, user_data);
+ flash_done (MM_SERIAL_PORT (port), NULL, user_data);
return;
} else if (task_priv->custom_init_fail_if_timeout) {
/* Fail the probe if the plugin wanted it and the command timed out */
- if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
probe_complete (task);
return;
}
@@ -569,7 +743,7 @@ custom_init_response (MMSerialPort *port,
}
/* Otherwise proceed to probing */
- mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data);
+ mm_at_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data);
}
static void
@@ -583,14 +757,14 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data)
if (task_priv->custom_init) {
if (!delay_secs)
delay_secs = 3;
- mm_serial_port_queue_command (port,
- task_priv->custom_init,
- delay_secs,
- custom_init_response,
- user_data);
+ mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port),
+ task_priv->custom_init,
+ delay_secs,
+ custom_init_response,
+ user_data);
} else {
/* Otherwise start normal probing */
- custom_init_response (port, NULL, NULL, user_data);
+ custom_init_response (MM_AT_SERIAL_PORT (port), NULL, NULL, user_data);
}
}
@@ -603,7 +777,7 @@ try_open (gpointer user_data)
task_priv->open_id = 0;
- if (!mm_serial_port_open (task_priv->probe_port, &error)) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (task_priv->probe_port), &error)) {
if (++task_priv->open_tries > 4) {
/* took too long to open the port; give up */
g_warning ("(%s): failed to open after 4 tries.",
@@ -611,7 +785,7 @@ try_open (gpointer user_data)
probe_complete (task);
} else if (g_error_matches (error,
MM_SERIAL_ERROR,
- MM_SERIAL_OPEN_FAILED_NO_DEVICE)) {
+ MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) {
/* this is nozomi being dumb; try again */
task_priv->open_id = g_timeout_add_seconds (1, try_open, task);
} else {
@@ -626,10 +800,13 @@ try_open (gpointer user_data)
port = mm_plugin_base_supports_task_get_port (task);
g_assert (port);
+ task_priv->full_id = g_signal_connect (task_priv->probe_port, "buffer-full",
+ G_CALLBACK (port_buffer_full), task);
+
g_debug ("(%s): probe requested by plugin '%s'",
g_udev_device_get_name (port),
mm_plugin_get_name (MM_PLUGIN (task_priv->plugin)));
- mm_serial_port_flash (task_priv->probe_port, 100, flash_done, task);
+ mm_serial_port_flash (MM_SERIAL_PORT (task_priv->probe_port), 100, TRUE, flash_done, task);
}
return FALSE;
@@ -641,7 +818,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
GError **error)
{
MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
- MMSerialPort *serial;
+ MMAtSerialPort *serial;
const char *name;
GUdevDevice *port;
@@ -654,7 +831,7 @@ mm_plugin_base_probe_port (MMPluginBase *self,
name = g_udev_device_get_name (port);
g_assert (name);
- serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
+ serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY);
if (serial == NULL) {
g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
"Failed to create new serial port.");
@@ -666,10 +843,10 @@ mm_plugin_base_probe_port (MMPluginBase *self,
MM_PORT_CARRIER_DETECT, FALSE,
NULL);
- mm_serial_port_set_response_parser (serial,
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
+ mm_at_serial_port_set_response_parser (serial,
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
/* Open the port */
task_priv->probe_port = serial;
@@ -695,20 +872,6 @@ mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self,
static void
modem_destroyed (gpointer data, GObject *modem)
{
- MMPluginBase *self = MM_PLUGIN_BASE (data);
- MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
- GHashTableIter iter;
- gpointer key, value;
-
- /* Remove it from the modems info */
- g_hash_table_iter_init (&iter, priv->modems);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- if (value == modem) {
- g_hash_table_iter_remove (&iter);
- break;
- }
- }
-
/* Since we don't track port cached capabilities on a per-modem basis,
* we just have to live with blowing away the cached capabilities whenever
* a modem gets removed. Could do better here by storing a structure
@@ -719,48 +882,6 @@ modem_destroyed (gpointer data, GObject *modem)
g_hash_table_remove_all (cached_caps);
}
-MMModem *
-mm_plugin_base_find_modem (MMPluginBase *self,
- const char *master_device)
-{
- MMPluginBasePrivate *priv;
-
- g_return_val_if_fail (self != NULL, NULL);
- g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL);
- g_return_val_if_fail (master_device != NULL, NULL);
- g_return_val_if_fail (strlen (master_device) > 0, NULL);
-
- priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
- return g_hash_table_lookup (priv->modems, master_device);
-}
-
-/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
-
-static int hex2num (char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- return -1;
-}
-
-static int hex2byte (const char *hex)
-{
- int a, b;
- a = hex2num(*hex++);
- if (a < 0)
- return -1;
- b = hex2num(*hex++);
- if (b < 0)
- return -1;
- return (a << 4) | b;
-}
-
-/* End from hostap */
-
gboolean
mm_plugin_base_get_device_ids (MMPluginBase *self,
const char *subsys,
@@ -793,8 +914,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self,
goto out;
if (vendor) {
- *vendor = (guint16) (hex2byte (vid + 2) & 0xFF);
- *vendor |= (guint16) ((hex2byte (vid) & 0xFF) << 8);
+ *vendor = (guint16) (utils_hex2byte (vid + 2) & 0xFF);
+ *vendor |= (guint16) ((utils_hex2byte (vid) & 0xFF) << 8);
}
pid = g_udev_device_get_property (device, "ID_MODEL_ID");
@@ -804,8 +925,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self,
}
if (product) {
- *product = (guint16) (hex2byte (pid + 2) & 0xFF);
- *product |= (guint16) ((hex2byte (pid) & 0xFF) << 8);
+ *product = (guint16) (utils_hex2byte (pid + 2) & 0xFF);
+ *product |= (guint16) ((utils_hex2byte (pid) & 0xFF) << 8);
}
success = TRUE;
@@ -859,58 +980,21 @@ get_driver_name (GUdevDevice *device)
return ret;
}
-static GUdevDevice *
-real_find_physical_device (MMPluginBase *plugin, GUdevDevice *child)
-{
- GUdevDevice *iter, *old = NULL;
- GUdevDevice *physdev = NULL;
- const char *subsys, *type;
- guint32 i = 0;
- gboolean is_usb = FALSE, is_pci = FALSE;
-
- g_return_val_if_fail (child != NULL, NULL);
-
- iter = g_object_ref (child);
- while (iter && i++ < 8) {
- subsys = g_udev_device_get_subsystem (iter);
- if (subsys) {
- if (is_usb || !strcmp (subsys, "usb")) {
- is_usb = TRUE;
- type = g_udev_device_get_devtype (iter);
- if (type && !strcmp (type, "usb_device")) {
- physdev = iter;
- break;
- }
- } else if (is_pci || !strcmp (subsys, "pci")) {
- is_pci = TRUE;
- physdev = iter;
- break;
- }
- }
-
- old = iter;
- iter = g_udev_device_get_parent (old);
- g_object_unref (old);
- }
-
- return physdev;
-}
-
static MMPluginSupportsResult
supports_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer callback_data)
{
MMPluginBase *self = MM_PLUGIN_BASE (plugin);
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
- GUdevDevice *port = NULL, *physdev = NULL;
+ GUdevDevice *port = NULL;
char *driver = NULL, *key = NULL;
MMPluginBaseSupportsTask *task;
MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- MMModem *existing;
- const char *master_path;
key = get_key (subsys, name);
task = g_hash_table_lookup (priv->tasks, key);
@@ -923,22 +1007,14 @@ supports_port (MMPlugin *plugin,
if (!port)
goto out;
- physdev = MM_PLUGIN_BASE_GET_CLASS (self)->find_physical_device (self, port);
- if (!physdev)
- goto out;
-
driver = get_driver_name (port);
if (!driver)
goto out;
- task = supports_task_new (self, port, physdev, driver, callback, callback_data);
+ task = supports_task_new (self, port, physdev_path, driver, callback, callback_data);
g_assert (task);
g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task));
- /* Help the plugin out a bit by finding an existing modem for this port */
- master_path = g_udev_device_get_sysfs_path (physdev);
- existing = g_hash_table_lookup (priv->modems, master_path);
-
result = MM_PLUGIN_BASE_GET_CLASS (self)->supports_port (self, existing, task);
if (result != MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS) {
/* If the plugin doesn't support the port at all, the supports task is
@@ -949,8 +1025,6 @@ supports_port (MMPlugin *plugin,
g_object_unref (task);
out:
- if (physdev)
- g_object_unref (physdev);
if (port)
g_object_unref (port);
g_free (key);
@@ -984,14 +1058,14 @@ static MMModem *
grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error)
{
MMPluginBase *self = MM_PLUGIN_BASE (plugin);
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
MMPluginBaseSupportsTask *task;
+ MMModem *modem = NULL;
char *key;
- MMModem *existing = NULL, *modem = NULL;
- const char *master_path;
key = get_key (subsys, name);
task = g_hash_table_lookup (priv->tasks, key);
@@ -1000,16 +1074,10 @@ grab_port (MMPlugin *plugin,
g_return_val_if_fail (task != NULL, FALSE);
}
- /* Help the plugin out a bit by finding an existing modem for this port */
- master_path = g_udev_device_get_sysfs_path (mm_plugin_base_supports_task_get_physdev (task));
- existing = g_hash_table_lookup (priv->modems, master_path);
-
/* Let the modem grab the port */
modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self, existing, task, error);
- if (modem && !existing) {
- g_hash_table_insert (priv->modems, g_strdup (master_path), modem);
+ if (modem && !existing)
g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self);
- }
g_hash_table_remove (priv->tasks, key);
g_free (key);
@@ -1039,7 +1107,6 @@ mm_plugin_base_init (MMPluginBase *self)
priv->client = g_udev_client_new (subsys);
- priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_object_unref);
}
@@ -1086,7 +1153,6 @@ finalize (GObject *object)
g_object_unref (priv->client);
- g_hash_table_destroy (priv->modems);
g_hash_table_destroy (priv->tasks);
G_OBJECT_CLASS (mm_plugin_base_parent_class)->finalize (object);
@@ -1099,7 +1165,6 @@ mm_plugin_base_class_init (MMPluginBaseClass *klass)
g_type_class_add_private (object_class, sizeof (MMPluginBasePrivate));
- klass->find_physical_device = real_find_physical_device;
klass->handle_probe_response = real_handle_probe_response;
/* Virtual methods */
diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h
index 49e68d4..a32d53b 100644
--- a/src/mm-plugin-base.h
+++ b/src/mm-plugin-base.h
@@ -37,6 +37,7 @@
#define MM_PLUGIN_BASE_PORT_CAP_W 0x0080 /* Wireless commands */
#define MM_PLUGIN_BASE_PORT_CAP_IS856 0x0100 /* CDMA 3G EVDO rev 0 */
#define MM_PLUGIN_BASE_PORT_CAP_IS856_A 0x0200 /* CDMA 3G EVDO rev A */
+#define MM_PLUGIN_BASE_PORT_CAP_QCDM 0x0400 /* QCDM-capable port */
#define MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK (mm_plugin_base_supports_task_get_type ())
#define MM_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTask))
@@ -59,7 +60,7 @@ MMPlugin *mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *tas
GUdevDevice *mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task);
-GUdevDevice *mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task);
+const char *mm_plugin_base_supports_task_get_physdev_path (MMPluginBaseSupportsTask *task);
const char *mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *task);
@@ -107,13 +108,6 @@ struct _MMPluginBaseClass {
void (*cancel_task) (MMPluginBase *plugin,
MMPluginBaseSupportsTask *task);
- /* Find a the physical device of a port, ie the USB or PCI or whatever
- * "master" device that owns the port. The GUdevDevice object returned
- * will be unref-ed by the caller.
- */
- GUdevDevice * (*find_physical_device) (MMPluginBase *plugin,
- GUdevDevice *port);
-
void (*handle_probe_response) (MMPluginBase *plugin,
MMPluginBaseSupportsTask *task,
const char *command,
@@ -128,9 +122,6 @@ struct _MMPluginBaseClass {
GType mm_plugin_base_get_type (void);
-MMModem *mm_plugin_base_find_modem (MMPluginBase *self,
- const char *master_device);
-
gboolean mm_plugin_base_get_device_ids (MMPluginBase *self,
const char *subsys,
const char *name,
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 830f2d6..7e5c582 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -28,15 +28,24 @@ MMPluginSupportsResult
mm_plugin_supports_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer user_data)
{
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
g_return_val_if_fail (subsys != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (physdev_path != NULL, FALSE);
g_return_val_if_fail (callback != NULL, FALSE);
- return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name, callback, user_data);
+ return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin,
+ subsys,
+ name,
+ physdev_path,
+ existing,
+ callback,
+ user_data);
}
void
@@ -55,13 +64,14 @@ MMModem *
mm_plugin_grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error)
{
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
g_return_val_if_fail (subsys != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
- return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, error);
+ return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, existing, error);
}
/*****************************************************************************/
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
index d1e85b6..4f1dfc0 100644
--- a/src/mm-plugin.h
+++ b/src/mm-plugin.h
@@ -70,6 +70,8 @@ struct _MMPlugin {
MMPluginSupportsResult (*supports_port) (MMPlugin *self,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer user_data);
@@ -94,6 +96,7 @@ struct _MMPlugin {
MMModem * (*grab_port) (MMPlugin *self,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error);
};
@@ -104,6 +107,8 @@ const char *mm_plugin_get_name (MMPlugin *plugin);
MMPluginSupportsResult mm_plugin_supports_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ const char *physdev_path,
+ MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer user_data);
@@ -114,6 +119,7 @@ void mm_plugin_cancel_supports_port (MMPlugin *plugin,
MMModem *mm_plugin_grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
+ MMModem *existing,
GError **error);
#endif /* MM_PLUGIN_H */
diff --git a/src/mm-port.c b/src/mm-port.c
index 7e8edc4..b2018fe 100644
--- a/src/mm-port.c
+++ b/src/mm-port.c
@@ -19,6 +19,7 @@
#include <string.h>
#include "mm-port.h"
+#include "mm-options.h"
G_DEFINE_TYPE (MMPort, mm_port, G_TYPE_OBJECT)
@@ -45,6 +46,26 @@ typedef struct {
/*****************************************************************************/
+const char *
+mm_port_type_to_name (MMPortType ptype)
+{
+ switch (ptype) {
+ case MM_PORT_TYPE_PRIMARY:
+ return "primary";
+ case MM_PORT_TYPE_SECONDARY:
+ return "secondary";
+ case MM_PORT_TYPE_IGNORED:
+ return "ignored";
+ case MM_PORT_TYPE_QCDM:
+ return "QCDM";
+ default:
+ break;
+ }
+ return "(unknown)";
+}
+
+/*****************************************************************************/
+
static GObject*
constructor (GType type,
guint n_construct_params,
@@ -140,6 +161,16 @@ mm_port_set_connected (MMPort *self, gboolean connected)
if (priv->connected != connected) {
priv->connected = connected;
g_object_notify (G_OBJECT (self), MM_PORT_CONNECTED);
+ if (mm_options_debug()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): port now %s",
+ tv.tv_sec,
+ tv.tv_usec,
+ priv->device,
+ connected ? "connected" : "disconnected");
+ }
}
}
diff --git a/src/mm-port.h b/src/mm-port.h
index b537618..43f78f4 100644
--- a/src/mm-port.h
+++ b/src/mm-port.h
@@ -33,8 +33,9 @@ typedef enum {
MM_PORT_TYPE_PRIMARY,
MM_PORT_TYPE_SECONDARY,
MM_PORT_TYPE_IGNORED,
+ MM_PORT_TYPE_QCDM,
- MM_PORT_TYPE_LAST = MM_PORT_TYPE_IGNORED
+ MM_PORT_TYPE_LAST = MM_PORT_TYPE_QCDM
} MMPortType;
#define MM_TYPE_PORT (mm_port_get_type ())
@@ -75,5 +76,7 @@ gboolean mm_port_get_connected (MMPort *self);
void mm_port_set_connected (MMPort *self, gboolean connected);
+const char * mm_port_type_to_name (MMPortType ptype);
+
#endif /* MM_PORT_H */
diff --git a/src/mm-qcdm-serial-port.c b/src/mm-qcdm-serial-port.c
new file mode 100644
index 0000000..1ce27e7
--- /dev/null
+++ b/src/mm-qcdm-serial-port.c
@@ -0,0 +1,225 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mm-qcdm-serial-port.h"
+#include "mm-errors.h"
+#include "mm-options.h"
+#include "libqcdm/src/com.h"
+#include "libqcdm/src/utils.h"
+
+G_DEFINE_TYPE (MMQcdmSerialPort, mm_qcdm_serial_port, MM_TYPE_SERIAL_PORT)
+
+#define MM_QCDM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPortPrivate))
+
+typedef struct {
+ gboolean foo;
+} MMQcdmSerialPortPrivate;
+
+
+/*****************************************************************************/
+
+static gboolean
+parse_response (MMSerialPort *port, GByteArray *response, GError **error)
+{
+ int i;
+
+ /* Look for the QCDM packet termination character; if we found it, treat
+ * the buffer as a qcdm command.
+ */
+ for (i = 0; i < response->len; i++) {
+ if (response->data[i] == 0x7E)
+ return TRUE;
+ }
+
+ /* Otherwise, need more data from the device */
+ return FALSE;
+}
+
+static gsize
+handle_response (MMSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data)
+{
+ MMQcdmSerialResponseFn response_callback = (MMQcdmSerialResponseFn) callback;
+ GByteArray *unescaped = NULL;
+ GError *dm_error = NULL;
+ gsize used = 0;
+
+ /* Ignore empty frames */
+ if (response->len > 0 && response->data[0] == 0x7E)
+ return 1;
+
+ if (!error) {
+ gboolean more = FALSE, success;
+ gsize unescaped_len = 0;
+
+ /* FIXME: don't munge around with byte array internals */
+ unescaped = g_byte_array_sized_new (1024);
+ success = dm_decapsulate_buffer ((const char *) response->data,
+ response->len,
+ (char *) unescaped->data,
+ 1024,
+ &unescaped_len,
+ &used,
+ &more);
+ if (!success) {
+ g_set_error_literal (&dm_error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Failed to unescape QCDM packet.");
+ g_byte_array_free (unescaped, TRUE);
+ unescaped = NULL;
+ } else if (more) {
+ /* Need more data; we shouldn't have gotten here since the parse
+ * function checks for the end-of-frame marker, but whatever.
+ */
+ return 0;
+ } else {
+ /* Successfully decapsulated the DM command */
+ unescaped->len = (guint) unescaped_len;
+ }
+ }
+
+ response_callback (MM_QCDM_SERIAL_PORT (port),
+ unescaped,
+ dm_error ? dm_error : error,
+ callback_data);
+
+ if (unescaped)
+ g_byte_array_free (unescaped, TRUE);
+
+ return used;
+}
+
+/*****************************************************************************/
+
+void
+mm_qcdm_serial_port_queue_command (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_QCDM_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ /* 'command' is expected to be already CRC-ed and escaped */
+ mm_serial_port_queue_command (MM_SERIAL_PORT (self),
+ command,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+void
+mm_qcdm_serial_port_queue_command_cached (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_QCDM_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ /* 'command' is expected to be already CRC-ed and escaped */
+ mm_serial_port_queue_command_cached (MM_SERIAL_PORT (self),
+ command,
+ TRUE,
+ timeout_seconds,
+ (MMSerialResponseFn) callback,
+ user_data);
+}
+
+static void
+debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len)
+{
+ static GString *debug = NULL;
+ const char *s = buf;
+ GTimeVal tv;
+
+ if (!debug)
+ debug = g_string_sized_new (512);
+
+ g_string_append (debug, prefix);
+
+ while (len--)
+ g_string_append_printf (debug, " %02x", (guint8) (*s++ & 0xFF));
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s): %s",
+ tv.tv_sec,
+ tv.tv_usec,
+ mm_port_get_device (MM_PORT (port)),
+ debug->str);
+ g_string_truncate (debug, 0);
+}
+
+/*****************************************************************************/
+
+static gboolean
+config_fd (MMSerialPort *port, int fd, GError **error)
+{
+ return qcdm_port_setup (fd, error);
+}
+
+/*****************************************************************************/
+
+MMQcdmSerialPort *
+mm_qcdm_serial_port_new (const char *name, MMPortType ptype)
+{
+ return MM_QCDM_SERIAL_PORT (g_object_new (MM_TYPE_QCDM_SERIAL_PORT,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
+ MM_PORT_TYPE, ptype,
+ NULL));
+}
+
+static void
+mm_qcdm_serial_port_init (MMQcdmSerialPort *self)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ G_OBJECT_CLASS (mm_qcdm_serial_port_parent_class)->finalize (object);
+}
+
+static void
+mm_qcdm_serial_port_class_init (MMQcdmSerialPortClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMSerialPortClass *port_class = MM_SERIAL_PORT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMQcdmSerialPortPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ port_class->parse_response = parse_response;
+ port_class->handle_response = handle_response;
+ port_class->config_fd = config_fd;
+ port_class->debug_log = debug_log;
+}
diff --git a/src/mm-qcdm-serial-port.h b/src/mm-qcdm-serial-port.h
new file mode 100644
index 0000000..e70e124
--- /dev/null
+++ b/src/mm-qcdm-serial-port.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_QCDM_SERIAL_PORT_H
+#define MM_QCDM_SERIAL_PORT_H
+
+#include <glib.h>
+#include <glib/gtypes.h>
+#include <glib-object.h>
+
+#include "mm-serial-port.h"
+
+#define MM_TYPE_QCDM_SERIAL_PORT (mm_qcdm_serial_port_get_type ())
+#define MM_QCDM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPort))
+#define MM_QCDM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPortClass))
+#define MM_IS_QCDM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_QCDM_SERIAL_PORT))
+#define MM_IS_QCDM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_QCDM_SERIAL_PORT))
+#define MM_QCDM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_QCDM_SERIAL_PORT, MMQcdmSerialPortClass))
+
+typedef struct _MMQcdmSerialPort MMQcdmSerialPort;
+typedef struct _MMQcdmSerialPortClass MMQcdmSerialPortClass;
+
+typedef void (*MMQcdmSerialResponseFn) (MMQcdmSerialPort *port,
+ GByteArray *response,
+ GError *error,
+ gpointer user_data);
+
+struct _MMQcdmSerialPort {
+ MMSerialPort parent;
+};
+
+struct _MMQcdmSerialPortClass {
+ MMSerialPortClass parent;
+};
+
+GType mm_qcdm_serial_port_get_type (void);
+
+MMQcdmSerialPort *mm_qcdm_serial_port_new (const char *name, MMPortType ptype);
+
+void mm_qcdm_serial_port_queue_command (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data);
+
+void mm_qcdm_serial_port_queue_command_cached (MMQcdmSerialPort *self,
+ GByteArray *command,
+ guint32 timeout_seconds,
+ MMQcdmSerialResponseFn callback,
+ gpointer user_data);
+
+#endif /* MM_QCDM_SERIAL_PORT_H */
+
diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c
index 58985d9..7c9598e 100644
--- a/src/mm-serial-parsers.c
+++ b/src/mm-serial-parsers.c
@@ -190,7 +190,8 @@ mm_serial_parser_v0_destroy (gpointer data)
typedef struct {
GRegex *regex_ok;
GRegex *regex_connect;
- GRegex *regex_detailed_error;
+ GRegex *regex_cme_error;
+ GRegex *regex_cme_error_str;
GRegex *regex_unknown_error;
GRegex *regex_connect_failed;
} MMSerialParserV1;
@@ -205,7 +206,8 @@ mm_serial_parser_v1_new (void)
parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+$", flags, 0, NULL);
parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL);
- parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL);
+ parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL);
+ parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR: ([^\\n\\r]+)\\r\\n$", flags, 0, NULL);
parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL);
parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL);
@@ -219,9 +221,10 @@ mm_serial_parser_v1_parse (gpointer data,
{
MMSerialParserV1 *parser = (MMSerialParserV1 *) data;
GMatchInfo *match_info;
- GError *local_error;
- int code;
+ GError *local_error = NULL;
gboolean found = FALSE;
+ char *str;
+ int code;
g_return_val_if_fail (parser != NULL, FALSE);
g_return_val_if_fail (response != NULL, FALSE);
@@ -243,56 +246,70 @@ mm_serial_parser_v1_parse (gpointer data,
}
/* Now failures */
- code = MM_MOBILE_ERROR_UNKNOWN;
- local_error = NULL;
- found = g_regex_match_full (parser->regex_detailed_error,
+ /* Numeric CME errors */
+ found = g_regex_match_full (parser->regex_cme_error,
response->str, response->len,
0, 0, &match_info, NULL);
-
if (found) {
- char *str;
+ str = g_match_info_fetch (match_info, 1);
+ g_assert (str);
+ local_error = mm_mobile_error_for_code (atoi (str));
+ g_free (str);
+ g_match_info_free (match_info);
+ goto done;
+ }
+ /* String CME errors */
+ found = g_regex_match_full (parser->regex_cme_error_str,
+ response->str, response->len,
+ 0, 0, &match_info, NULL);
+ if (found) {
str = g_match_info_fetch (match_info, 1);
- if (str) {
- code = atoi (str);
- g_free (str);
- }
+ g_assert (str);
+ local_error = mm_mobile_error_for_string (str);
+ g_free (str);
g_match_info_free (match_info);
- } else
- found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, NULL, NULL);
+ goto done;
+ }
- if (found)
- local_error = mm_mobile_error_for_code (code);
- else {
- found = g_regex_match_full (parser->regex_connect_failed,
- response->str, response->len,
- 0, 0, &match_info, NULL);
- if (found) {
- char *str;
+ /* Last resort; unknown error */
+ found = g_regex_match_full (parser->regex_unknown_error,
+ response->str, response->len,
+ 0, 0, NULL, NULL);
+ if (found) {
+ local_error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
+ goto done;
+ }
- str = g_match_info_fetch (match_info, 1);
- if (str) {
- if (!strcmp (str, "NO CARRIER"))
- code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
- else if (!strcmp (str, "BUSY"))
- code = MM_MODEM_CONNECT_ERROR_BUSY;
- else if (!strcmp (str, "NO ANSWER"))
- code = MM_MODEM_CONNECT_ERROR_NO_ANSWER;
- else if (!strcmp (str, "NO DIALTONE"))
- code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE;
- else
- /* uhm... make something up (yes, ok, lie!). */
- code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
+ /* Connection failures */
+ found = g_regex_match_full (parser->regex_connect_failed,
+ response->str, response->len,
+ 0, 0, &match_info, NULL);
+ if (found) {
+ str = g_match_info_fetch (match_info, 1);
+ g_assert (str);
+
+ if (!strcmp (str, "NO CARRIER"))
+ code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
+ else if (!strcmp (str, "BUSY"))
+ code = MM_MODEM_CONNECT_ERROR_BUSY;
+ else if (!strcmp (str, "NO ANSWER"))
+ code = MM_MODEM_CONNECT_ERROR_NO_ANSWER;
+ else if (!strcmp (str, "NO DIALTONE"))
+ code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE;
+ else {
+ /* uhm... make something up (yes, ok, lie!). */
+ code = MM_MODEM_CONNECT_ERROR_NO_CARRIER;
+ }
- g_free (str);
- }
- g_match_info_free (match_info);
+ g_free (str);
+ g_match_info_free (match_info);
- local_error = mm_modem_connect_error_for_code (code);
- }
+ local_error = mm_modem_connect_error_for_code (code);
}
+done:
if (found)
response_clean (response);
@@ -313,7 +330,8 @@ mm_serial_parser_v1_destroy (gpointer data)
g_regex_unref (parser->regex_ok);
g_regex_unref (parser->regex_connect);
- g_regex_unref (parser->regex_detailed_error);
+ g_regex_unref (parser->regex_cme_error);
+ g_regex_unref (parser->regex_cme_error_str);
g_regex_unref (parser->regex_unknown_error);
g_regex_unref (parser->regex_connect_failed);
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
index 2600ae5..ed44167 100644
--- a/src/mm-serial-port.c
+++ b/src/mm-serial-port.c
@@ -11,14 +11,14 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#define _GNU_SOURCE /* for strcasestr() */
#include <stdio.h>
#include <stdlib.h>
-#include <termio.h>
+#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -51,20 +51,12 @@ enum {
#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate))
typedef struct {
+ guint32 open_count;
int fd;
GHashTable *reply_cache;
GIOChannel *channel;
GQueue *queue;
- GString *command;
- GString *response;
-
- gboolean connected;
-
- /* Response parser data */
- MMSerialResponseParserFn response_parser_fn;
- gpointer response_parser_user_data;
- GDestroyNotify response_parser_notify;
- GSList *unsolicited_msg_handlers;
+ GByteArray *response;
struct termios old_t;
@@ -148,12 +140,12 @@ void
mm_serial_port_print_config (MMSerialPort *port, const char *detail)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (port);
- struct termio stbuf;
+ struct termios stbuf;
int err;
- err = ioctl (priv->fd, TCGETA, &stbuf);
+ err = tcgetattr (priv->fd, &stbuf);
if (err) {
- g_warning ("*** %s (%s): (%s) TCGETA error %d",
+ g_warning ("*** %s (%s): (%s) tcgetattr() error %d",
__func__, detail, mm_port_get_device (MM_PORT (port)), errno);
return;
}
@@ -165,33 +157,6 @@ mm_serial_port_print_config (MMSerialPort *port, const char *detail)
}
#endif
-typedef struct {
- GRegex *regex;
- MMSerialUnsolicitedMsgFn callback;
- gpointer user_data;
- GDestroyNotify notify;
-} MMUnsolicitedMsgHandler;
-
-static void
-mm_serial_port_set_cached_reply (MMSerialPort *self,
- const char *command,
- const char *reply)
-{
- if (reply)
- g_hash_table_insert (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache,
- g_strdup (command),
- g_strdup (reply));
- else
- g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
-}
-
-static const char *
-mm_serial_port_get_cached_reply (MMSerialPort *self,
- const char *command)
-{
- return (char *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
-}
-
static int
parse_baudrate (guint i)
{
@@ -327,10 +292,10 @@ parse_stopbits (guint i)
}
static gboolean
-config_fd (MMSerialPort *self, GError **error)
+real_config_fd (MMSerialPort *self, int fd, GError **error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- struct termio stbuf;
+ struct termios stbuf;
int speed;
int bits;
int parity;
@@ -341,9 +306,9 @@ config_fd (MMSerialPort *self, GError **error)
parity = parse_parity (priv->parity);
stopbits = parse_stopbits (priv->stopbits);
- memset (&stbuf, 0, sizeof (struct termio));
- if (ioctl (priv->fd, TCGETA, &stbuf) != 0) {
- g_warning ("%s (%s): TCGETA error: %d",
+ memset (&stbuf, 0, sizeof (struct termios));
+ if (tcgetattr (fd, &stbuf) != 0) {
+ g_warning ("%s (%s): tcgetattr() error: %d",
__func__,
mm_port_get_device (MM_PORT (self)),
errno);
@@ -357,10 +322,16 @@ config_fd (MMSerialPort *self, GError **error)
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
- stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
+ /* Use software handshaking */
+ stbuf.c_iflag |= (IXON | IXOFF | IXANY);
- if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
+ /* Set up port speed and serial attributes; also ignore modem control
+ * lines since most drivers don't implement RTS/CTS anyway.
+ */
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS);
+ stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits | CLOCAL);
+
+ if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
g_set_error (error,
MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
@@ -373,105 +344,99 @@ config_fd (MMSerialPort *self, GError **error)
}
static void
-serial_debug (MMSerialPort *self, const char *prefix, const char *buf, int len)
+serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len)
{
- static GString *debug = NULL;
- const char *s;
-
- if (!mm_options_debug ())
- return;
-
- if (len < 0)
- len = strlen (buf);
-
- if (!debug)
- debug = g_string_sized_new (256);
-
- g_string_append (debug, prefix);
- g_string_append (debug, " '");
-
- s = buf;
- while (len--) {
- if (g_ascii_isprint (*s))
- g_string_append_c (debug, *s);
- else if (*s == '\r')
- g_string_append (debug, "<CR>");
- else if (*s == '\n')
- g_string_append (debug, "<LF>");
- else
- g_string_append_printf (debug, "\\%d", *s);
-
- s++;
- }
+ g_return_if_fail (len > 0);
- g_string_append_c (debug, '\'');
- g_debug ("(%s): %s", mm_port_get_device (MM_PORT (self)), debug->str);
- g_string_truncate (debug, 0);
+ if (mm_options_debug () && MM_SERIAL_PORT_GET_CLASS (self)->debug_log)
+ MM_SERIAL_PORT_GET_CLASS (self)->debug_log (self, prefix, buf, len);
}
static gboolean
mm_serial_port_send_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
GError **error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- const char *s;
- int status;
+ int status, i = 0;
int eagain_count = 1000;
+ const guint8 *p;
if (priv->fd < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"%s", "Sending command failed: device is not enabled");
return FALSE;
}
if (mm_port_get_connected (MM_PORT (self))) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"%s", "Sending command failed: device is connected");
return FALSE;
}
- g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2);
- g_string_append (priv->command, command);
-
- if (command[strlen (command)] != '\r')
- g_string_append_c (priv->command, '\r');
-
- serial_debug (self, "-->", priv->command->str, -1);
+ serial_debug (self, "-->", (const char *) command->data, command->len);
/* Only accept about 3 seconds of EAGAIN */
if (priv->send_delay > 0)
eagain_count = 3000000 / priv->send_delay;
- s = priv->command->str;
- while (*s) {
- status = write (priv->fd, s, 1);
+ while (i < command->len) {
+ p = &command->data[i];
+ status = write (priv->fd, p, 1);
if (status < 0) {
if (errno == EAGAIN) {
eagain_count--;
if (eagain_count <= 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"Sending command failed: '%s'", strerror (errno));
break;
}
} else {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
"Sending command failed: '%s'", strerror (errno));
break;
}
} else
- s++;
+ i++;
if (priv->send_delay)
usleep (priv->send_delay);
}
- return *s == '\0';
+ return i == command->len;
+}
+
+static void
+mm_serial_port_set_cached_reply (MMSerialPort *self,
+ const GByteArray *command,
+ const GByteArray *response)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ if (response) {
+ GByteArray *cmd_copy = g_byte_array_sized_new (command->len);
+ GByteArray *rsp_copy = g_byte_array_sized_new (response->len);
+
+ g_byte_array_append (cmd_copy, command->data, command->len);
+ g_byte_array_append (rsp_copy, response->data, response->len);
+ g_hash_table_insert (priv->reply_cache, cmd_copy, rsp_copy);
+ } else
+ g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
+}
+
+static const GByteArray *
+mm_serial_port_get_cached_reply (MMSerialPort *self, GByteArray *command)
+{
+ return (const GByteArray *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
}
typedef struct {
- char *command;
- MMSerialResponseFn callback;
+ GByteArray *command;
+ GCallback callback;
gpointer user_data;
guint32 timeout;
gboolean cached;
@@ -500,11 +465,25 @@ mm_serial_port_schedule_queue_process (MMSerialPort *self)
g_source_unref (source);
}
+static gsize
+real_handle_response (MMSerialPort *self,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data)
+{
+ MMSerialResponseFn response_callback = (MMSerialResponseFn) callback;
+
+ response_callback (self, response, error, callback_data);
+ return response->len;
+}
+
static void
mm_serial_port_got_response (MMSerialPort *self, GError *error)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
MMQueueData *info;
+ gsize consumed = priv->response->len;
if (priv->timeout_id) {
g_source_remove (priv->timeout_id);
@@ -514,19 +493,26 @@ mm_serial_port_got_response (MMSerialPort *self, GError *error)
info = (MMQueueData *) g_queue_pop_head (priv->queue);
if (info) {
if (info->cached && !error)
- mm_serial_port_set_cached_reply (self, info->command, priv->response->str);
-
- if (info->callback)
- info->callback (self, priv->response, error, info->user_data);
+ mm_serial_port_set_cached_reply (self, info->command, priv->response);
+
+ if (info->callback) {
+ g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->handle_response != NULL);
+ consumed = MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
+ priv->response,
+ error,
+ info->callback,
+ info->user_data);
+ }
- g_free (info->command);
+ g_byte_array_free (info->command, TRUE);
g_slice_free (MMQueueData, info);
}
if (error)
g_error_free (error);
- g_string_truncate (priv->response, 0);
+ if (consumed)
+ g_byte_array_remove_range (priv->response, 0, consumed);
if (!g_queue_is_empty (priv->queue))
mm_serial_port_schedule_queue_process (self);
}
@@ -541,7 +527,7 @@ mm_serial_port_timed_out (gpointer data)
priv->timeout_id = 0;
error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_RESPONSE_TIMEOUT,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
"Serial command timed out");
/* FIXME: This is not completely correct - if the response finally arrives and there's
some other command waiting for response right now, the other command will
@@ -566,10 +552,10 @@ mm_serial_port_queue_process (gpointer data)
return FALSE;
if (info->cached) {
- const char *cached = mm_serial_port_get_cached_reply (self, info->command);
+ const GByteArray *cached = mm_serial_port_get_cached_reply (self, info->command);
if (cached) {
- g_string_append (priv->response, cached);
+ g_byte_array_append (priv->response, cached->data, cached->len);
mm_serial_port_got_response (self, NULL);
return FALSE;
}
@@ -590,111 +576,16 @@ mm_serial_port_queue_process (gpointer data)
return FALSE;
}
-void
-mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self,
- GRegex *regex,
- MMSerialUnsolicitedMsgFn callback,
- gpointer user_data,
- GDestroyNotify notify)
-{
- MMUnsolicitedMsgHandler *handler;
- MMSerialPortPrivate *priv;
-
- g_return_if_fail (MM_IS_SERIAL_PORT (self));
- g_return_if_fail (regex != NULL);
-
- handler = g_slice_new (MMUnsolicitedMsgHandler);
- handler->regex = g_regex_ref (regex);
- handler->callback = callback;
- handler->user_data = user_data;
- handler->notify = notify;
-
- priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
-}
-
-void
-mm_serial_port_set_response_parser (MMSerialPort *self,
- MMSerialResponseParserFn fn,
- gpointer user_data,
- GDestroyNotify notify)
-{
- MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
-
- g_return_if_fail (MM_IS_SERIAL_PORT (self));
-
- if (priv->response_parser_notify)
- priv->response_parser_notify (priv->response_parser_user_data);
-
- priv->response_parser_fn = fn;
- priv->response_parser_user_data = user_data;
- priv->response_parser_notify = notify;
-}
-
-static gboolean
-remove_eval_cb (const GMatchInfo *match_info,
- GString *result,
- gpointer user_data)
-{
- int *result_len = (int *) user_data;
- int start;
- int end;
-
- if (g_match_info_fetch_pos (match_info, 0, &start, &end))
- *result_len -= (end - start);
-
- return FALSE;
-}
-
-static void
-parse_unsolicited_messages (MMSerialPort *self,
- GString *response)
-{
- MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- GSList *iter;
-
- for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
- MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data;
- GMatchInfo *match_info;
- gboolean matches;
-
- matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL);
- if (handler->callback) {
- while (g_match_info_matches (match_info)) {
- handler->callback (self, match_info, handler->user_data);
- g_match_info_next (match_info, NULL);
- }
- }
-
- g_match_info_free (match_info);
-
- if (matches) {
- /* Remove matches */
- char *str;
- int result_len = response->len;
-
- str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0,
- remove_eval_cb, &result_len, NULL);
-
- g_string_truncate (response, 0);
- g_string_append_len (response, str, result_len);
- g_free (str);
- }
- }
-}
-
static gboolean
parse_response (MMSerialPort *self,
- GString *response,
+ GByteArray *response,
GError **error)
{
- MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
-
- g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
-
- parse_unsolicited_messages (self, response);
+ if (MM_SERIAL_PORT_GET_CLASS (self)->parse_unsolicited)
+ MM_SERIAL_PORT_GET_CLASS (self)->parse_unsolicited (self, response);
- return priv->response_parser_fn (priv->response_parser_user_data, response, error);
+ g_return_val_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->parse_response, FALSE);
+ return MM_SERIAL_PORT_GET_CLASS (self)->parse_response (self, response, error);
}
static gboolean
@@ -709,13 +600,15 @@ data_available (GIOChannel *source,
GIOStatus status;
if (condition & G_IO_HUP) {
- g_string_truncate (priv->response, 0);
- mm_serial_port_close (self);
+ if (priv->response->len)
+ g_byte_array_remove_range (priv->response, 0, priv->response->len);
+ mm_serial_port_close_force (self);
return FALSE;
}
if (condition & G_IO_ERR) {
- g_string_truncate (priv->response, 0);
+ if (priv->response->len)
+ g_byte_array_remove_range (priv->response, 0, priv->response->len);
return TRUE;
}
@@ -724,9 +617,13 @@ data_available (GIOChannel *source,
status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
if (status == G_IO_STATUS_ERROR) {
- g_warning ("%s", err->message);
- g_error_free (err);
- err = NULL;
+ if (err && err->message)
+ g_warning ("%s", err->message);
+ g_clear_error (&err);
+
+ /* Serial port is closed; we're done */
+ if (priv->watch_id == 0)
+ break;
}
/* If no bytes read, just let g_io_channel wait for more data */
@@ -735,14 +632,14 @@ data_available (GIOChannel *source,
if (bytes_read > 0) {
serial_debug (self, "<--", buf, bytes_read);
- g_string_append_len (priv->response, buf, bytes_read);
+ g_byte_array_append (priv->response, (const guint8 *) buf, bytes_read);
}
- /* Make sure the string doesn't grow too long */
- if (priv->response->len > SERIAL_BUF_SIZE) {
- g_warning ("%s (%s): response buffer filled before repsonse received",
- G_STRFUNC, mm_port_get_device (MM_PORT (self)));
- g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2));
+ /* Make sure the response doesn't grow too long */
+ if (priv->response->len > SERIAL_BUF_SIZE) {
+ /* Notify listeners and then trim the buffer */
+ g_signal_emit_by_name (self, "buffer-full", priv->response);
+ g_byte_array_remove_range (priv->response, 0, (SERIAL_BUF_SIZE / 2));
}
if (parse_response (self, priv->response, &err))
@@ -792,13 +689,13 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- if (priv->fd >= 0) {
+ device = mm_port_get_device (MM_PORT (self));
+
+ if (priv->open_count) {
/* Already open */
- return TRUE;
+ goto success;
}
- device = mm_port_get_device (MM_PORT (self));
-
g_message ("(%s) opening serial device...", device);
devfile = g_strdup_printf ("/dev/%s", device);
errno = 0;
@@ -812,32 +709,29 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
*/
g_set_error (error,
MM_SERIAL_ERROR,
- (errno == ENODEV) ? MM_SERIAL_OPEN_FAILED_NO_DEVICE : MM_SERIAL_OPEN_FAILED,
+ (errno == ENODEV) ? MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE : MM_SERIAL_ERROR_OPEN_FAILED,
"Could not open serial device %s: %s", device, strerror (errno));
return FALSE;
}
if (ioctl (priv->fd, TIOCEXCL) < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
"Could not lock serial device %s: %s", device, strerror (errno));
- close (priv->fd);
- priv->fd = -1;
- return FALSE;
+ goto error;
}
- if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ /* Flush any waiting IO */
+ tcflush (priv->fd, TCIOFLUSH);
+
+ if (tcgetattr (priv->fd, &priv->old_t) < 0) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
"Could not open serial device %s: %s", device, strerror (errno));
- close (priv->fd);
- priv->fd = -1;
- return FALSE;
+ goto error;
}
- if (!config_fd (self, error)) {
- close (priv->fd);
- priv->fd = -1;
- return FALSE;
- }
+ g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->config_fd);
+ if (!MM_SERIAL_PORT_GET_CLASS (self)->config_fd (self, priv->fd, error))
+ goto error;
priv->channel = g_io_channel_unix_new (priv->fd);
g_io_channel_set_encoding (priv->channel, NULL, NULL);
@@ -849,17 +743,58 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
priv->connected_id = g_signal_connect (self, "notify::" MM_PORT_CONNECTED,
G_CALLBACK (port_connected), NULL);
+success:
+ priv->open_count++;
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s) device open count is %d (open)",
+ tv.tv_sec, tv.tv_usec, device, priv->open_count);
+ }
return TRUE;
+
+error:
+ close (priv->fd);
+ priv->fd = -1;
+ return FALSE;
+}
+
+gboolean
+mm_serial_port_is_open (MMSerialPort *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
+
+ return !!MM_SERIAL_PORT_GET_PRIVATE (self)->open_count;
}
void
mm_serial_port_close (MMSerialPort *self)
{
MMSerialPortPrivate *priv;
+ const char *device;
+ int i;
g_return_if_fail (MM_IS_SERIAL_PORT (self));
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ g_return_if_fail (priv->open_count > 0);
+
+ device = mm_port_get_device (MM_PORT (self));
+
+ priv->open_count--;
+
+ if (mm_options_debug ()) {
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+ g_debug ("<%ld.%ld> (%s) device open count is %d (close)",
+ tv.tv_sec, tv.tv_usec, device, priv->open_count);
+ }
+
+ if (priv->open_count > 0)
+ return;
if (priv->connected_id) {
g_signal_handler_disconnect (self, priv->connected_id);
@@ -867,31 +802,74 @@ mm_serial_port_close (MMSerialPort *self)
}
if (priv->fd >= 0) {
- g_message ("(%s) closing serial device...", mm_port_get_device (MM_PORT (self)));
+ g_message ("(%s) closing serial device...", device);
mm_port_set_connected (MM_PORT (self), FALSE);
if (priv->channel) {
g_source_remove (priv->watch_id);
+ priv->watch_id = 0;
g_io_channel_shutdown (priv->channel, TRUE, NULL);
g_io_channel_unref (priv->channel);
priv->channel = NULL;
}
- if (priv->flash_id > 0) {
- g_source_remove (priv->flash_id);
- priv->flash_id = 0;
- }
+ mm_serial_port_flash_cancel (self);
- ioctl (priv->fd, TCSETA, &priv->old_t);
+ tcsetattr (priv->fd, TCSANOW, &priv->old_t);
close (priv->fd);
priv->fd = -1;
}
+
+ /* Clear the command queue */
+ for (i = 0; i < g_queue_get_length (priv->queue); i++) {
+ MMQueueData *item = g_queue_peek_nth (priv->queue, i);
+
+ if (item->callback) {
+ GError *error;
+ GByteArray *response;
+
+ g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->handle_response != NULL);
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_SEND_FAILED,
+ "Serial port is now closed");
+ response = g_byte_array_sized_new (1);
+ g_byte_array_append (response, (const guint8 *) "\0", 1);
+ MM_SERIAL_PORT_GET_CLASS (self)->handle_response (self,
+ response,
+ error,
+ item->callback,
+ item->user_data);
+ g_error_free (error);
+ g_byte_array_free (response, TRUE);
+ }
+
+ g_byte_array_free (item->command, TRUE);
+ g_slice_free (MMQueueData, item);
+ }
+ g_queue_clear (priv->queue);
+}
+
+void
+mm_serial_port_close_force (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ g_return_if_fail (priv->open_count > 0);
+
+ /* Force the port to close */
+ priv->open_count = 1;
+ mm_serial_port_close (self);
}
static void
internal_queue_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
gboolean cached,
guint32 timeout_seconds,
MMSerialResponseFn callback,
@@ -904,15 +882,20 @@ internal_queue_command (MMSerialPort *self,
g_return_if_fail (command != NULL);
info = g_slice_new0 (MMQueueData);
- info->command = g_strdup (command);
+ if (take_command)
+ info->command = command;
+ else {
+ info->command = g_byte_array_sized_new (command->len);
+ g_byte_array_append (info->command, command->data, command->len);
+ }
info->cached = cached;
info->timeout = timeout_seconds;
- info->callback = callback;
+ info->callback = (GCallback) callback;
info->user_data = user_data;
/* Clear the cached value for this command if not asking for cached value */
if (!cached)
- mm_serial_port_set_cached_reply (self, command, NULL);
+ mm_serial_port_set_cached_reply (self, info->command, NULL);
g_queue_push_tail (priv->queue, info);
@@ -922,22 +905,24 @@ internal_queue_command (MMSerialPort *self,
void
mm_serial_port_queue_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data)
{
- internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data);
+ internal_queue_command (self, command, take_command, FALSE, timeout_seconds, callback, user_data);
}
void
mm_serial_port_queue_command_cached (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data)
{
- internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data);
+ internal_queue_command (self, command, take_command, TRUE, timeout_seconds, callback, user_data);
}
typedef struct {
@@ -1030,8 +1015,14 @@ flash_do (gpointer data)
priv->flash_id = 0;
- if (!set_speed (info->port, info->current_speed, &error))
- g_assert (error);
+ if (info->current_speed) {
+ if (!set_speed (info->port, info->current_speed, &error))
+ g_assert (error);
+ } else {
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_FLASH_FAILED,
+ "Failed to retrieve current speed");
+ }
info->callback (info->port, error, info->user_data);
g_clear_error (&error);
@@ -1042,6 +1033,7 @@ flash_do (gpointer data)
gboolean
mm_serial_port_flash (MMSerialPort *self,
guint32 flash_time,
+ gboolean ignore_errors,
MMSerialFlashFn callback,
gpointer user_data)
{
@@ -1049,12 +1041,22 @@ mm_serial_port_flash (MMSerialPort *self,
MMSerialPortPrivate *priv;
speed_t cur_speed = 0;
GError *error = NULL;
+ gboolean success;
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
g_return_val_if_fail (callback != NULL, FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ if (!mm_serial_port_is_open (self)) {
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_NOT_OPEN,
+ "The serial port is not open.");
+ callback (self, error, user_data);
+ g_error_free (error);
+ return FALSE;
+ }
+
if (priv->flash_id > 0) {
error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_OPERATION_IN_PROGRESS,
@@ -1064,11 +1066,13 @@ mm_serial_port_flash (MMSerialPort *self,
return FALSE;
}
- if (!get_speed (self, &cur_speed, &error)) {
+ success = get_speed (self, &cur_speed, &error);
+ if (!success && !ignore_errors) {
callback (self, error, user_data);
g_error_free (error);
return FALSE;
}
+ g_clear_error (&error);
info = g_slice_new0 (FlashInfo);
info->port = self;
@@ -1076,7 +1080,8 @@ mm_serial_port_flash (MMSerialPort *self,
info->callback = callback;
info->user_data = user_data;
- if (!set_speed (self, B0, &error)) {
+ success = set_speed (self, B0, &error);
+ if (!success && !ignore_errors) {
callback (self, error, user_data);
g_error_free (error);
return FALSE;
@@ -1113,12 +1118,54 @@ mm_serial_port_new (const char *name, MMPortType ptype)
NULL));
}
+static gboolean
+ba_equal (gconstpointer v1, gconstpointer v2)
+{
+ const GByteArray *a = v1;
+ const GByteArray *b = v2;
+
+ if (!a && b)
+ return -1;
+ else if (a && !b)
+ return 1;
+ else if (!a && !b)
+ return 0;
+
+ g_assert (a && b);
+ if (a->len < b->len)
+ return -1;
+ else if (a->len > b->len)
+ return 1;
+
+ g_assert (a->len == b->len);
+ return !memcmp (a->data, b->data, a->len);
+}
+
+static guint
+ba_hash (gconstpointer v)
+{
+ /* 31 bit hash function */
+ const GByteArray *array = v;
+ guint32 i, h = (const signed char) array->data[0];
+
+ for (i = 1; i < array->len; i++)
+ h = (h << 5) - h + (const signed char) array->data[i];
+
+ return h;
+}
+
+static void
+ba_free (gpointer v)
+{
+ g_byte_array_free ((GByteArray *) v, TRUE);
+}
+
static void
mm_serial_port_init (MMSerialPort *self)
{
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
- priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->reply_cache = g_hash_table_new_full (ba_hash, ba_equal, ba_free, ba_free);
priv->fd = -1;
priv->baud = 57600;
@@ -1128,8 +1175,7 @@ mm_serial_port_init (MMSerialPort *self)
priv->send_delay = 1000;
priv->queue = g_queue_new ();
- priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE);
- priv->response = g_string_sized_new (SERIAL_BUF_SIZE);
+ priv->response = g_byte_array_sized_new (500);
}
static void
@@ -1191,7 +1237,10 @@ get_property (GObject *object, guint prop_id,
static void
dispose (GObject *object)
{
- mm_serial_port_close (MM_SERIAL_PORT (object));
+ if (mm_serial_port_is_open (MM_SERIAL_PORT (object)))
+ mm_serial_port_close_force (MM_SERIAL_PORT (object));
+
+ mm_serial_port_flash_cancel (MM_SERIAL_PORT (object));
G_OBJECT_CLASS (mm_serial_port_parent_class)->dispose (object);
}
@@ -1203,24 +1252,8 @@ finalize (GObject *object)
MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
g_hash_table_destroy (priv->reply_cache);
+ g_byte_array_free (priv->response, TRUE);
g_queue_free (priv->queue);
- g_string_free (priv->command, TRUE);
- g_string_free (priv->response, TRUE);
-
- while (priv->unsolicited_msg_handlers) {
- MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
-
- if (handler->notify)
- handler->notify (handler->user_data);
-
- g_regex_unref (handler->regex);
- g_slice_free (MMUnsolicitedMsgHandler, handler);
- priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
- priv->unsolicited_msg_handlers);
- }
-
- if (priv->response_parser_notify)
- priv->response_parser_notify (priv->response_parser_user_data);
G_OBJECT_CLASS (mm_serial_port_parent_class)->finalize (object);
}
@@ -1238,6 +1271,9 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
object_class->dispose = dispose;
object_class->finalize = finalize;
+ klass->config_fd = real_config_fd;
+ klass->handle_response = real_handle_response;
+
/* Properties */
g_object_class_install_property
(object_class, PROP_BAUD,
@@ -1278,4 +1314,14 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
"Send delay",
0, G_MAXUINT64, 0,
G_PARAM_READWRITE));
+
+ /* Signals */
+ g_signal_new ("buffer-full",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
}
+
diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h
index 841b4fa..f78f793 100644
--- a/src/mm-serial-port.h
+++ b/src/mm-serial-port.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_SERIAL_PORT_H
@@ -39,67 +39,101 @@
typedef struct _MMSerialPort MMSerialPort;
typedef struct _MMSerialPortClass MMSerialPortClass;
-typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
- GString *response,
- GError **error);
-
-typedef void (*MMSerialUnsolicitedMsgFn) (MMSerialPort *port,
- GMatchInfo *match_info,
- gpointer user_data);
-
-typedef void (*MMSerialResponseFn) (MMSerialPort *port,
- GString *response,
+typedef void (*MMSerialFlashFn) (MMSerialPort *port,
GError *error,
gpointer user_data);
-typedef void (*MMSerialFlashFn) (MMSerialPort *port,
+typedef void (*MMSerialResponseFn) (MMSerialPort *port,
+ GByteArray *response,
GError *error,
gpointer user_data);
+
struct _MMSerialPort {
MMPort parent;
};
struct _MMSerialPortClass {
MMPortClass parent;
+
+ /* Called for subclasses to parse unsolicited responses. If any recognized
+ * unsolicited response is found, it should be removed from the 'response'
+ * byte array before returning.
+ */
+ void (*parse_unsolicited) (MMSerialPort *self, GByteArray *response);
+
+ /* Called to parse the device's response to a command or determine if the
+ * response was an error response. If the response indicates an error, an
+ * appropriate error should be returned in the 'error' argument. The
+ * function should return FALSE if there is not enough data yet to determine
+ * the device's reply (whether success *or* error), and should return TRUE
+ * when the device's response has been recognized and parsed.
+ */
+ gboolean (*parse_response) (MMSerialPort *self,
+ GByteArray *response,
+ GError **error);
+
+ /* Called after parsing to allow the command response to be delivered to
+ * it's callback to be handled. Returns the # of bytes of the response
+ * consumed.
+ */
+ gsize (*handle_response) (MMSerialPort *self,
+ GByteArray *response,
+ GError *error,
+ GCallback callback,
+ gpointer callback_data);
+
+ /* Called to configure the serial port after it's opened. On error, should
+ * return FALSE and set 'error' as appropriate.
+ */
+ gboolean (*config_fd) (MMSerialPort *self, int fd, GError **error);
+
+ void (*debug_log) (MMSerialPort *self,
+ const char *prefix,
+ const char *buf,
+ gsize len);
+
+ /* Signals */
+ void (*buffer_full) (MMSerialPort *port, const GByteArray *buffer);
};
GType mm_serial_port_get_type (void);
MMSerialPort *mm_serial_port_new (const char *name, MMPortType ptype);
-void mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self,
- GRegex *regex,
- MMSerialUnsolicitedMsgFn callback,
- gpointer user_data,
- GDestroyNotify notify);
+/* Keep in mind that port open/close is refcounted, so ensure that
+ * open/close calls are properly balanced.
+ */
-void mm_serial_port_set_response_parser (MMSerialPort *self,
- MMSerialResponseParserFn fn,
- gpointer user_data,
- GDestroyNotify notify);
+gboolean mm_serial_port_is_open (MMSerialPort *self);
gboolean mm_serial_port_open (MMSerialPort *self,
GError **error);
void mm_serial_port_close (MMSerialPort *self);
+
+void mm_serial_port_close_force (MMSerialPort *self);
+
+gboolean mm_serial_port_flash (MMSerialPort *self,
+ guint32 flash_time,
+ gboolean ignore_errors,
+ MMSerialFlashFn callback,
+ gpointer user_data);
+void mm_serial_port_flash_cancel (MMSerialPort *self);
+
void mm_serial_port_queue_command (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data);
void mm_serial_port_queue_command_cached (MMSerialPort *self,
- const char *command,
+ GByteArray *command,
+ gboolean take_command,
guint32 timeout_seconds,
MMSerialResponseFn callback,
gpointer user_data);
-gboolean mm_serial_port_flash (MMSerialPort *self,
- guint32 flash_time,
- MMSerialFlashFn callback,
- gpointer user_data);
-void mm_serial_port_flash_cancel (MMSerialPort *self);
-
#endif /* MM_SERIAL_PORT_H */
diff --git a/src/mm-utils.c b/src/mm-utils.c
new file mode 100644
index 0000000..56182c0
--- /dev/null
+++ b/src/mm-utils.c
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "mm-utils.h"
+
+/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
+
+static int hex2num (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+int utils_hex2byte (const char *hex)
+{
+ int a, b;
+ a = hex2num(*hex++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+char *
+utils_hexstr2bin (const char *hex, gsize *out_len)
+{
+ size_t len = strlen (hex);
+ size_t i;
+ int a;
+ const char * ipos = hex;
+ char * buf = NULL;
+ char * opos;
+
+ /* Length must be a multiple of 2 */
+ g_return_val_if_fail ((len % 2) == 0, NULL);
+
+ opos = buf = g_malloc0 ((len / 2) + 1);
+ for (i = 0; i < len; i += 2) {
+ a = utils_hex2byte (ipos);
+ if (a < 0) {
+ g_free (buf);
+ return NULL;
+ }
+ *opos++ = a;
+ ipos += 2;
+ }
+ *out_len = len / 2;
+ return buf;
+}
+
+/* End from hostap */
+
diff --git a/src/mm-utils.h b/src/mm-utils.h
new file mode 100644
index 0000000..79e7827
--- /dev/null
+++ b/src/mm-utils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef MM_UTILS_H
+#define MM_UTILS_H
+
+int utils_hex2byte (const char *hex);
+
+char *utils_hexstr2bin (const char *hex, gsize *out_len);
+
+#endif /* MM_UTILS_H */
+
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index 3d93423..92a7af8 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -18,6 +18,11 @@
#include "mm-modem-helpers.h"
+typedef struct {
+ GPtrArray *solicited_creg;
+ GPtrArray *unsolicited_creg;
+} TestData;
+
#define MM_SCAN_TAG_STATUS "status"
#define MM_SCAN_TAG_OPER_LONG "operator-long"
#define MM_SCAN_TAG_OPER_SHORT "operator-short"
@@ -35,10 +40,10 @@ typedef struct {
#define ARRAY_LEN(i) (sizeof (i) / sizeof (i[0]))
static void
-test_results (const char *desc,
- const char *reply,
- OperEntry *expected_results,
- guint32 expected_results_len)
+test_cops_results (const char *desc,
+ const char *reply,
+ OperEntry *expected_results,
+ guint32 expected_results_len)
{
guint i;
GError *error = NULL;
@@ -101,7 +106,7 @@ test_cops_response_tm506 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -113,7 +118,7 @@ test_cops_response_gt3gplus (void *f, gpointer d)
{ "1", "Cingular", "Cingular", "310410", "0" },
};
- test_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -126,7 +131,7 @@ test_cops_response_ac881 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -139,7 +144,7 @@ test_cops_response_gtmax36 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -152,7 +157,7 @@ test_cops_response_ac860 (void *f, gpointer d)
{ "1", "Cingular", "Cinglr", "310410", "0" },
};
- test_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -165,7 +170,7 @@ test_cops_response_gtm378 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -177,7 +182,7 @@ test_cops_response_motoc (void *f, gpointer d)
{ "0", "Cingular Wireless", NULL, "310410", NULL },
};
- test_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -189,7 +194,7 @@ test_cops_response_mf627a (void *f, gpointer d)
{ "3", "Voicestream Wireless Corporation", "VSTREAM", "31026", "0" },
};
- test_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -201,7 +206,7 @@ test_cops_response_mf627b (void *f, gpointer d)
{ "3", NULL, NULL, "31026", "0" },
};
- test_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -213,7 +218,7 @@ test_cops_response_e160g (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -226,7 +231,7 @@ test_cops_response_mercury (void *f, gpointer d)
{ "1", "T-Mobile", "TMO", "31026", "0" },
};
- test_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -239,7 +244,7 @@ test_cops_response_quicksilver (void *f, gpointer d)
{ "1", "AT&T", NULL, "310260", "0" },
};
- test_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -251,7 +256,7 @@ test_cops_response_icon225 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -265,7 +270,7 @@ test_cops_response_icon452 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -278,7 +283,7 @@ test_cops_response_f3507g (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "2" }
};
- test_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -291,7 +296,7 @@ test_cops_response_f3607gw (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -304,7 +309,7 @@ test_cops_response_mc8775 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -317,7 +322,7 @@ test_cops_response_n80 (void *f, gpointer d)
{ "1", "Cingular", NULL, "31041", NULL },
};
- test_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -329,7 +334,7 @@ test_cops_response_e1550 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -341,7 +346,7 @@ test_cops_response_mf622 (void *f, gpointer d)
{ "1", NULL, NULL, "310410", "0" },
};
- test_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -354,7 +359,7 @@ test_cops_response_e226 (void *f, gpointer d)
{ "1", NULL, NULL, "310410", "0" },
};
- test_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -367,7 +372,7 @@ test_cops_response_xu870 (void *f, gpointer d)
{ "1", "T-Mobile", "TMO", "31026", "0" },
};
- test_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -380,7 +385,7 @@ test_cops_response_gtultraexpress (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -392,7 +397,7 @@ test_cops_response_n2720 (void *f, gpointer d)
{ "1", "AT&T", NULL, "310410", "0" },
};
- test_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -405,7 +410,28 @@ test_cops_response_gobi (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected));
+}
+
+static void
+test_cops_response_sek600i (void *f, gpointer d)
+{
+ /* Phone is stupid enough to support 3G but not report cell technology,
+ * mixing together 2G and 3G cells without any way of distinguishing
+ * which is which...
+ */
+ const char *reply = "+COPS: (2,\"blau\",\"\",\"26203\"),(2,\"blau\",\"\",\"26203\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26202\"),(3,\"\",\"\",\"26207\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26207\")";
+ static OperEntry expected[] = {
+ { "2", "blau", NULL, "26203", NULL },
+ { "2", "blau", NULL, "26203", NULL },
+ { "3", NULL, NULL, "26201", NULL },
+ { "3", NULL, NULL, "26202", NULL },
+ { "3", NULL, NULL, "26207", NULL },
+ { "3", NULL, NULL, "26201", NULL },
+ { "3", NULL, NULL, "26207", NULL },
+ };
+
+ test_cops_results ("Sony-Ericsson K600i", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -432,6 +458,338 @@ test_cops_response_umts_invalid (void *f, gpointer d)
g_assert (error == NULL);
}
+typedef struct {
+ guint32 state;
+ gulong lac;
+ gulong ci;
+ gint act;
+
+ guint regex_num;
+ gboolean cgreg;
+} CregResult;
+
+static void
+test_creg_match (const char *test,
+ gboolean solicited,
+ const char *reply,
+ TestData *data,
+ const CregResult *result)
+{
+ int i;
+ GMatchInfo *info = NULL;
+ guint32 state = 0;
+ gulong lac = 0, ci = 0;
+ gint access_tech = -1;
+ GError *error = NULL;
+ gboolean success, cgreg = FALSE;
+ guint regex_num = 0;
+ GPtrArray *array;
+
+ g_assert (reply);
+ g_assert (test);
+ g_assert (data);
+ g_assert (result);
+
+ g_print ("\nTesting %s +CREG %s response...\n",
+ test,
+ solicited ? "solicited" : "unsolicited");
+
+ array = solicited ? data->solicited_creg : data->unsolicited_creg;
+ for (i = 0; i < array->len; i++) {
+ GRegex *r = g_ptr_array_index (array, i);
+
+ if (g_regex_match (r, reply, 0, &info)) {
+ regex_num = i + 1;
+ break;
+ }
+ g_match_info_free (info);
+ info = NULL;
+ }
+
+ g_assert (info != NULL);
+ g_assert (regex_num == result->regex_num);
+
+ success = mm_gsm_parse_creg_response (info, &state, &lac, &ci, &access_tech, &cgreg, &error);
+ g_assert (success);
+ g_assert (error == NULL);
+ g_assert (state == result->state);
+ g_assert (lac == result->lac);
+ g_assert (ci == result->ci);
+ g_assert (access_tech == result->act);
+ g_assert (cgreg == result->cgreg);
+}
+
+static void
+test_creg1_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 1,3";
+ const CregResult result = { 3, 0, 0, -1 , 2, FALSE};
+
+ test_creg_match ("CREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_creg1_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 3\r\n";
+ const CregResult result = { 3, 0, 0, -1 , 1, FALSE};
+
+ test_creg_match ("CREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_mercury_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 0,1,84CD,00D30173";
+ const CregResult result = { 1, 0x84cd, 0xd30173, -1 , 4, FALSE};
+
+ test_creg_match ("Sierra Mercury CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_mercury_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 1,84CD,00D30156\r\n";
+ const CregResult result = { 1, 0x84cd, 0xd30156, -1 , 3, FALSE};
+
+ test_creg_match ("Sierra Mercury CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_sek850i_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,\"CE00\",\"01CEAD8F\"";
+ const CregResult result = { 1, 0xce00, 0x01cead8f, -1 , 4, FALSE};
+
+ test_creg_match ("Sony Ericsson K850i CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_sek850i_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 1,\"CE00\",\"00005449\"\r\n";
+ const CregResult result = { 1, 0xce00, 0x5449, -1 , 3, FALSE};
+
+ test_creg_match ("Sony Ericsson K850i CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_solicited_unregistered (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,0,00,0";
+ const CregResult result = { 0, 0, 0, -1 , 4, FALSE};
+
+ test_creg_match ("Huawei E160G unregistered CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,8BE3,2BAF";
+ const CregResult result = { 1, 0x8be3, 0x2baf, -1 , 4, FALSE};
+
+ test_creg_match ("Huawei E160G CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,8BE3,2BAF\r\n";
+ const CregResult result = { 2, 0x8be3, 0x2baf, -1 , 3, FALSE};
+
+ test_creg_match ("Huawei E160G CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_tm506_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,\"8BE3\",\"00002BAF\"";
+ const CregResult result = { 1, 0x8BE3, 0x2BAF, -1 , 4, FALSE};
+
+ /* Test leading zeros in the CI */
+ test_creg_match ("Sony Ericsson TM-506 CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_xu870_unsolicited_unregistered (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,,\r\n";
+ const CregResult result = { 2, 0, 0, -1 , 3, FALSE};
+
+ test_creg_match ("Novatel XU870 unregistered CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg1_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CGREG: 1,3";
+ const CregResult result = { 3, 0, 0, -1 , 2, TRUE};
+
+ test_creg_match ("CGREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg1_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 3\r\n";
+ const CregResult result = { 3, 0, 0, -1 , 1, TRUE};
+
+ test_creg_match ("CGREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg2_f3607gw_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CGREG: 2,1,\"8BE3\",\"00002B5D\",3";
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, 3 , 6, TRUE};
+
+ test_creg_match ("Ericsson F3607gw CGREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg2_f3607gw_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 1,\"8BE3\",\"00002B5D\",3\r\n";
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, 3 , 5, TRUE};
+
+ test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_md400_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,5,\"0502\",\"0404736D\"\r\n";
+ const CregResult result = { 5, 0x0502, 0x0404736D, -1 , 4, FALSE};
+
+ test_creg_match ("Sony-Ericsson MD400 CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg2_md400_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 5,\"0502\",\"0404736D\",2\r\n";
+ const CregResult result = { 5, 0x0502, 0x0404736D, 2, 5, TRUE};
+
+ test_creg_match ("Sony-Ericsson MD400 CGREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg_cgreg_multi_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 5\r\n\r\n+CGREG: 0\r\n";
+ const CregResult result = { 5, 0, 0, -1, 1, FALSE};
+
+ test_creg_match ("Multi CREG/CGREG", FALSE, reply, data, &result);
+}
+
+static void
+test_creg_cgreg_multi2_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 0\r\n\r\n+CREG: 5\r\n";
+ const CregResult result = { 0, 0, 0, -1, 1, TRUE};
+
+ test_creg_match ("Multi CREG/CGREG #2", FALSE, reply, data, &result);
+}
+
+static void
+test_cscs_icon225_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\")\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == (MM_MODEM_CHARSET_IRA |
+ MM_MODEM_CHARSET_GSM |
+ MM_MODEM_CHARSET_UCS2));
+}
+
+static void
+test_cscs_sierra_mercury_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: (\"IRA\",\"GSM\",\"UCS2\",\"PCCP437\")\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == (MM_MODEM_CHARSET_IRA |
+ MM_MODEM_CHARSET_GSM |
+ MM_MODEM_CHARSET_UCS2 |
+ MM_MODEM_CHARSET_PCCP437));
+}
+
+static void
+test_cscs_buslink_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: (\"8859-1\",\"ASCII\",\"GSM\",\"UCS2\",\"UTF8\")\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == (MM_MODEM_CHARSET_8859_1 |
+ MM_MODEM_CHARSET_IRA |
+ MM_MODEM_CHARSET_GSM |
+ MM_MODEM_CHARSET_UCS2 |
+ MM_MODEM_CHARSET_UTF8));
+}
+
+static void
+test_cscs_blackberry_support_response (void *f, gpointer d)
+{
+ const char *reply = "\r\n+CSCS: \"IRA\"\r\n";
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+ gboolean success;
+
+ success = mm_gsm_parse_cscs_support_response (reply, &charsets);
+ g_assert (success);
+
+ g_assert (charsets == MM_MODEM_CHARSET_IRA);
+}
+
+static TestData *
+test_data_new (void)
+{
+ TestData *data;
+
+ data = g_malloc0 (sizeof (TestData));
+ data->solicited_creg = mm_gsm_creg_regex_get (TRUE);
+ data->unsolicited_creg = mm_gsm_creg_regex_get (FALSE);
+ return data;
+}
+
+static void
+test_data_free (TestData *data)
+{
+ mm_gsm_creg_regex_destroy (data->solicited_creg);
+ mm_gsm_creg_regex_destroy (data->unsolicited_creg);
+ g_free (data);
+}
+
typedef void (*TCFunc)(void);
@@ -440,10 +798,13 @@ typedef void (*TCFunc)(void);
int main (int argc, char **argv)
{
GTestSuite *suite;
+ TestData *data;
+ gint result;
g_test_init (&argc, &argv, NULL);
suite = g_test_get_root ();
+ data = test_data_new ();
g_test_suite_add (suite, TESTCASE (test_cops_response_tm506, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gt3gplus, NULL));
@@ -470,10 +831,42 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cops_response_gtultraexpress, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_n2720, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gobi, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cops_response_sek600i, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gsm_invalid, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_umts_invalid, NULL));
- return g_test_run ();
+ g_test_suite_add (suite, TESTCASE (test_creg1_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg1_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_mercury_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_mercury_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited_unregistered, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_tm506_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_xu870_unsolicited_unregistered, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_md400_unsolicited, data));
+
+ g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_md400_unsolicited, data));
+
+ g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi2_unsolicited, data));
+
+ g_test_suite_add (suite, TESTCASE (test_cscs_icon225_support_response, data));
+ g_test_suite_add (suite, TESTCASE (test_cscs_sierra_mercury_support_response, data));
+ g_test_suite_add (suite, TESTCASE (test_cscs_buslink_support_response, data));
+ g_test_suite_add (suite, TESTCASE (test_cscs_blackberry_support_response, data));
+
+ result = g_test_run ();
+
+ test_data_free (data);
+
+ return result;
}
diff --git a/test/lsudev.c b/test/lsudev.c
index 02d9ab2..f5e4001 100644
--- a/test/lsudev.c
+++ b/test/lsudev.c
@@ -55,21 +55,22 @@ static void
println (guint indent, const char *fmt, ...)
{
va_list args;
- char real_fmt[1000];
+ GString *output;
int i;
g_return_if_fail (fmt != NULL);
- g_return_if_fail (indent < sizeof (real_fmt) - 2 - strlen (fmt));
+
+ output = g_string_sized_new (250);
for (i = 0; i < indent; i++)
- real_fmt[i] = ' ';
- strcpy (&real_fmt[i], fmt);
- real_fmt[i + strlen (fmt)] = '\n';
- real_fmt[i + strlen (fmt) + 1] = '\0';
+ g_string_append_c (output, ' ');
va_start (args, fmt);
- vprintf (real_fmt, args);
+ g_string_append_vprintf (output, fmt, args);
va_end (args);
+
+ g_print ("%s\n", output->str);
+ g_string_free (output, TRUE);
}
static void
@@ -88,7 +89,7 @@ dump_device_and_parent (GUdevDevice *device, guint indent)
println (indent, "Path: %s", g_udev_device_get_sysfs_path (device));
println (indent, "Driver: %s", g_udev_device_get_driver (device));
println (indent, "Action: %s", g_udev_device_get_action (device));
- println (indent, "Seq Num: %s", g_udev_device_get_seqnum (device));
+ println (indent, "Seq Num: %lu", g_udev_device_get_seqnum (device));
println (indent, "Dev File: %s", g_udev_device_get_device_file (device));
println (indent, "");