From 7fbee6ce27176bfc7ae9b34a4de9452cf5f6fa43 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Wed, 5 Feb 2014 08:38:23 +0100 Subject: Imported Upstream version 0.4+git.20110124t203624.00b6cce --- .gitignore | 2 + Makefile.am | 21 +- NEWS | 25 + configure.ac | 25 +- header-generator.xsl | 241 ++++ introspection/Makefile.am | 25 +- introspection/all.xml | 28 +- introspection/mm-manager.xml | 40 - introspection/mm-modem-cdma.xml | 97 -- introspection/mm-modem-gsm-card.xml | 127 -- introspection/mm-modem-gsm-contacts.xml | 104 -- introspection/mm-modem-gsm-hso.xml | 19 - introspection/mm-modem-gsm-network.xml | 340 ----- introspection/mm-modem-gsm-sms.xml | 156 --- introspection/mm-modem-gsm-ussd.xml | 78 -- introspection/mm-modem-gsm.xml | 164 --- introspection/mm-modem-location.xml | 253 ---- introspection/mm-modem-simple.xml | 58 - introspection/mm-modem.xml | 191 --- introspection/org.freedesktop.DBus.Properties.xml | 45 + .../org.freedesktop.ModemManager.Modem.Cdma.xml | 220 ++++ ...org.freedesktop.ModemManager.Modem.Gsm.Card.xml | 135 ++ ...freedesktop.ModemManager.Modem.Gsm.Contacts.xml | 104 ++ .../org.freedesktop.ModemManager.Modem.Gsm.Hso.xml | 19 + ....freedesktop.ModemManager.Modem.Gsm.Network.xml | 340 +++++ .../org.freedesktop.ModemManager.Modem.Gsm.SMS.xml | 156 +++ ...org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml | 84 ++ .../org.freedesktop.ModemManager.Modem.Gsm.xml | 167 +++ ...org.freedesktop.ModemManager.Modem.Location.xml | 253 ++++ .../org.freedesktop.ModemManager.Modem.Simple.xml | 58 + .../org.freedesktop.ModemManager.Modem.xml | 245 ++++ introspection/org.freedesktop.ModemManager.xml | 40 + libqcdm/src/Makefile.am | 3 - libqcdm/src/commands.c | 285 +++++ libqcdm/src/commands.h | 111 +- libqcdm/src/dm-commands.h | 68 + libqcdm/src/error.c | 1 + libqcdm/src/error.h | 1 + libqcdm/src/libqcdm.ver | 6 - libqcdm/src/nv-items.h | 29 +- libqcdm/tests/test-qcdm-com.c | 281 ++++- libqcdm/tests/test-qcdm-com.h | 8 + libqcdm/tests/test-qcdm-utils.c | 20 + libqcdm/tests/test-qcdm-utils.h | 2 + libqcdm/tests/test-qcdm.c | 9 + marshallers/Makefile.am | 4 +- marshallers/mm-marshal.list | 1 + plugins/77-mm-ericsson-mbm.rules | 33 + plugins/77-mm-x22x-port-types.rules | 30 + plugins/77-mm-zte-port-types.rules | 57 + plugins/Makefile.am | 71 +- plugins/mm-modem-anydata-cdma.c | 34 +- plugins/mm-modem-anydata-cdma.h | 4 +- plugins/mm-modem-gobi-gsm.c | 6 +- plugins/mm-modem-gobi-gsm.h | 4 +- plugins/mm-modem-hso.c | 8 +- plugins/mm-modem-hso.h | 4 +- plugins/mm-modem-huawei-cdma.c | 13 +- plugins/mm-modem-huawei-cdma.h | 4 +- plugins/mm-modem-huawei-gsm.c | 88 +- plugins/mm-modem-huawei-gsm.h | 4 +- plugins/mm-modem-icera.c | 794 ++++++++++++ plugins/mm-modem-icera.h | 89 ++ plugins/mm-modem-linktop.c | 208 ++++ plugins/mm-modem-linktop.h | 45 + plugins/mm-modem-longcheer-gsm.c | 6 +- plugins/mm-modem-longcheer-gsm.h | 4 +- plugins/mm-modem-mbm.c | 79 +- plugins/mm-modem-mbm.h | 4 +- plugins/mm-modem-moto-c-gsm.c | 6 +- plugins/mm-modem-moto-c-gsm.h | 4 +- plugins/mm-modem-nokia.c | 10 +- plugins/mm-modem-nokia.h | 4 +- plugins/mm-modem-novatel-cdma.c | 148 ++- plugins/mm-modem-novatel-cdma.h | 4 +- plugins/mm-modem-novatel-gsm.c | 6 +- plugins/mm-modem-novatel-gsm.h | 4 +- plugins/mm-modem-option.c | 6 +- plugins/mm-modem-option.h | 4 +- plugins/mm-modem-sierra-cdma.c | 6 +- plugins/mm-modem-sierra-cdma.h | 4 +- plugins/mm-modem-sierra-gsm.c | 318 ++++- plugins/mm-modem-sierra-gsm.h | 4 +- plugins/mm-modem-simtech-gsm.c | 6 +- plugins/mm-modem-simtech-gsm.h | 4 +- plugins/mm-modem-x22x-gsm.c | 209 ++++ plugins/mm-modem-x22x-gsm.h | 45 + plugins/mm-modem-zte.c | 206 ++- plugins/mm-modem-zte.h | 4 +- plugins/mm-plugin-anydata.c | 10 +- plugins/mm-plugin-generic.c | 33 +- plugins/mm-plugin-gobi.c | 14 +- plugins/mm-plugin-hso.c | 10 +- plugins/mm-plugin-huawei.c | 29 +- plugins/mm-plugin-linktop.c | 164 +++ plugins/mm-plugin-linktop.h | 41 + plugins/mm-plugin-longcheer.c | 14 +- plugins/mm-plugin-mbm.c | 12 +- plugins/mm-plugin-moto-c.c | 10 +- plugins/mm-plugin-nokia.c | 14 +- plugins/mm-plugin-novatel.c | 14 +- plugins/mm-plugin-option.c | 10 +- plugins/mm-plugin-sierra.c | 50 +- plugins/mm-plugin-simtech.c | 14 +- plugins/mm-plugin-x22x.c | 192 +++ plugins/mm-plugin-x22x.h | 41 + plugins/mm-plugin-zte.c | 35 +- policy/org.freedesktop.modem-manager.policy.in | 9 + src/77-mm-usb-device-blacklist.rules | 6 + src/80-mm-candidate.rules | 16 + src/Makefile.am | 92 +- src/main.c | 132 +- src/mm-at-serial-port.c | 12 +- src/mm-auth-provider-polkit.c | 27 +- src/mm-auth-provider.h | 1 + src/mm-charsets.c | 353 +++++- src/mm-charsets.h | 16 +- src/mm-generic-cdma.c | 285 +++-- src/mm-generic-cdma.h | 8 +- src/mm-generic-gsm.c | 1310 ++++++++++++++++++-- src/mm-generic-gsm.h | 31 +- src/mm-log.c | 233 ++++ src/mm-log.h | 61 + src/mm-manager.c | 119 +- src/mm-modem-base.c | 201 ++- src/mm-modem-cdma.c | 44 + src/mm-modem-cdma.h | 26 + src/mm-modem-gsm-card.c | 8 + src/mm-modem-gsm-card.h | 1 + src/mm-modem-gsm-network.c | 2 + src/mm-modem-gsm-ussd.c | 378 ++++++ src/mm-modem-gsm-ussd.h | 75 ++ src/mm-modem-gsm.h | 3 +- src/mm-modem-helpers.c | 296 ++++- src/mm-modem-helpers.h | 18 + src/mm-modem-location.c | 5 +- src/mm-modem-location.h | 7 +- src/mm-modem.c | 99 +- src/mm-modem.h | 22 +- src/mm-options.c | 55 - src/mm-options.h | 23 - src/mm-plugin-base.c | 91 +- src/mm-port.c | 30 +- src/mm-port.h | 2 + src/mm-properties-changed-signal.c | 150 ++- src/mm-properties-changed-signal.h | 18 +- src/mm-qcdm-serial-port.c | 134 +- src/mm-qcdm-serial-port.h | 2 + src/mm-serial-parsers.c | 28 +- src/mm-serial-port.c | 87 +- src/mm-serial-port.h | 1 + src/mm-utils.c | 14 + src/mm-utils.h | 2 + src/tests/Makefile.am | 31 +- src/tests/test-charsets.c | 323 +++++ src/tests/test-modem-helpers.c | 412 ++++++ src/tests/test-qcdm-serial-port.c | 482 +++++++ test/Makefile.am | 8 +- test/disable.py | 27 + test/enable.py | 27 + test/info.py | 256 ++++ test/list-modems.py | 54 + test/location.py | 57 + test/scan.py | 101 ++ test/ussd.py | 44 + 165 files changed, 11761 insertions(+), 2581 deletions(-) create mode 100644 header-generator.xsl delete mode 100644 introspection/mm-manager.xml delete mode 100644 introspection/mm-modem-cdma.xml delete mode 100644 introspection/mm-modem-gsm-card.xml delete mode 100644 introspection/mm-modem-gsm-contacts.xml delete mode 100644 introspection/mm-modem-gsm-hso.xml delete mode 100644 introspection/mm-modem-gsm-network.xml delete mode 100644 introspection/mm-modem-gsm-sms.xml delete mode 100644 introspection/mm-modem-gsm-ussd.xml delete mode 100644 introspection/mm-modem-gsm.xml delete mode 100644 introspection/mm-modem-location.xml delete mode 100644 introspection/mm-modem-simple.xml delete mode 100644 introspection/mm-modem.xml create mode 100644 introspection/org.freedesktop.DBus.Properties.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Cdma.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Gsm.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Location.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.Simple.xml create mode 100644 introspection/org.freedesktop.ModemManager.Modem.xml create mode 100644 introspection/org.freedesktop.ModemManager.xml delete mode 100644 libqcdm/src/libqcdm.ver create mode 100644 plugins/77-mm-x22x-port-types.rules create mode 100644 plugins/mm-modem-icera.c create mode 100644 plugins/mm-modem-icera.h create mode 100644 plugins/mm-modem-linktop.c create mode 100644 plugins/mm-modem-linktop.h create mode 100644 plugins/mm-modem-x22x-gsm.c create mode 100644 plugins/mm-modem-x22x-gsm.h create mode 100644 plugins/mm-plugin-linktop.c create mode 100644 plugins/mm-plugin-linktop.h create mode 100644 plugins/mm-plugin-x22x.c create mode 100644 plugins/mm-plugin-x22x.h create mode 100644 src/80-mm-candidate.rules create mode 100644 src/mm-log.c create mode 100644 src/mm-log.h create mode 100644 src/mm-modem-gsm-ussd.c create mode 100644 src/mm-modem-gsm-ussd.h delete mode 100644 src/mm-options.c delete mode 100644 src/mm-options.h create mode 100644 src/tests/test-charsets.c create mode 100644 src/tests/test-qcdm-serial-port.c create mode 100755 test/disable.py create mode 100755 test/enable.py create mode 100755 test/info.py create mode 100755 test/list-modems.py create mode 100755 test/location.py create mode 100755 test/scan.py create mode 100755 test/ussd.py diff --git a/.gitignore b/.gitignore index dd0a8bb..d8a6a13 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ docs/spec.html callouts/mm-modem-probe test/lsudev src/tests/test-modem-helpers +src/tests/test-charsets +src/tests/test-qcdm-serial-port policy/org.freedesktop.modem-manager.policy libqcdm/tests/test-qcdm diff --git a/Makefile.am b/Makefile.am index 276a276..4132bc9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,11 @@ +XSLTPROC = xsltproc --xinclude --nonet + +XMLS = $(wildcard introspection/*.xml) + if WITH_DOCS all: -XSLTPROC = xsltproc --xinclude --nonet - -XMLS = $(wildcard introspection/mm-*.xml) # Figure out if we need ASYNC_INTROSPECT and add it later GENERATED_FILES = \ @@ -42,16 +43,27 @@ dbusactivationdir = $(datadir)/dbus-1/system-services dbusactivation_in_files = org.freedesktop.ModemManager.service.in dbusactivation_DATA = $(dbusactivation_in_files:.service.in=.service) +includedir = @includedir@/mm + +include_HEADERS = include/mm-modem.h + +include/mm-modem.h: $(XMLS) introspection/all.xml header-generator.xsl + @install -d include + $(XSLTPROC) header-generator.xsl introspection/all.xml > $@ + %service: %service.in $(edit) $< >$@ +xmldir = $(datadir)/dbus-1/interfaces +xml_DATA = $(XMLS) + edit = @sed \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ -e 's|@libexecdir[@]|$(libexecdir)|g' -DISTCHECK_CONFIGURE_FLAGS = --with-udev-base-dir=$dc_install_base +DISTCHECK_CONFIGURE_FLAGS = --with-udev-base-dir=$dc_install_base --with-tests=yes INTLTOOL_FILES = \ intltool-extract.in \ @@ -68,6 +80,7 @@ DISTCLEANFILES = \ EXTRA_DIST = \ doc-generator.xsl \ + header-generator.xsl \ $(dbusactivation_in_files) \ $(INTLTOOL_FILES) \ $(dbusservice_file_polkit) \ diff --git a/NEWS b/NEWS index e69de29..45c2da3 100644 --- a/NEWS +++ b/NEWS @@ -0,0 +1,25 @@ +Overview of changes in ModemManager 0.5 +---------------------------------------- + +- Added a mechanism to get remaining incorrect PIN attempts +- Added a mechanism to retrieve the equipment identifier (MEID/ESN, IMEI) +- Added support for returning devices to factory settings +- Better compatibility with Blackberry GSM devices +- Added a Location Services API with support for GSM devices +- Fixes for Novatel CDMA devices (home/roaming status, better signal quality reporting) +- Better detection of EVDO registration when device is in 1X mode +- Add support for newer Alcatel devices like X220D +- Support for more ZTE devices +- Fix modem detection failures with some Sierra devices +- Add support for newer Ericsson devices +- Add an SIM identifier for use when auto-unlocking modems +- Add an device identifier for use when MEID/IMEI is not yet available +- Handle signal quality reporting for GSM modems that don't support AT+CSQ +- Add preliminary USSD support +- Fix reconnection attempts with some Sierra devices +- Add support for pseduo-ethernet interface on newer Sierra devices + +Fixed bugs: +rh#597296 rh#583691 rh#597088 bgo#626421 rh#585394 bgo#628105 bgo#627935 +bgo#621815 rh#632516 lp:682282 bgo#590798 + diff --git a/configure.ac b/configure.ac index b763d7b..4f8b94f 100644 --- a/configure.ac +++ b/configure.ac @@ -2,6 +2,7 @@ AC_PREREQ(2.52) AC_INIT(ModemManager, 0.4, dcbw@redhat.com, ModemManager) AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE AC_CONFIG_MACRO_DIR([m4]) @@ -26,7 +27,7 @@ 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(MM, dbus-glib-1 >= 0.86 glib-2.0 >= 2.18 gmodule-2.0 gobject-2.0) PKG_CHECK_MODULES(GUDEV, gudev-1.0) AC_SUBST(GUDEV_CFLAGS) @@ -63,12 +64,22 @@ case $with_polkit in AC_DEFINE(WITH_POLKIT, 1, [Define if you want to use PolicyKit]) AC_SUBST(POLKIT_CFLAGS) AC_SUBST(POLKIT_LIBS) + + # Check for polkit_authority_get_sync() + AC_CHECK_LIB([polkit-gobject-1], [polkit_authority_get_sync], ac_have_pk_auth_get_sync="1", ac_have_pk_auth_get_sync="0") + AC_DEFINE_UNQUOTED(HAVE_POLKIT_AUTHORITY_GET_SYNC, $ac_have_pk_auth_get_sync, [Define if you have a polkit with polkit_authority_get_sync()]) ;; *) with_polkit=no ;; esac +dnl +dnl Checks for new dbus-glib property access function +dnl +AC_CHECK_LIB([dbus-glib-1], [dbus_glib_global_set_disable_legacy_property_access], ac_have_dg_prop="1", ac_have_dg_prop="0") +AC_DEFINE_UNQUOTED(HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS, $ac_have_dg_prop, [Define if you have a dbus-glib with dbus_glib_global_set_disable_legacy_property_access()]) + # 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") @@ -114,18 +125,6 @@ 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 diff --git a/header-generator.xsl b/header-generator.xsl new file mode 100644 index 0000000..67f915f --- /dev/null +++ b/header-generator.xsl @@ -0,0 +1,241 @@ + + + + + + + + + + + + + +/* */ + + + + + + + + + + + + + + + + + + + + + + + + + + +#define "" + + +/* flag values */ + + + + + + + + + + + + + + + + + +#define + + + + + +/* enum values */ + + + + + + + + + + + + + + + + + +#define + + + + + + + + + + + + (generic description) + + + (Undocumented.) + + + + + + + + + + + + + + + MANAGER + + + +#define "" + + + + + + + + + + + + MANAGER + + + +#define "" + + + + + + + +/* Generated Header file do not edit */ +/* */ + +/* + * version + */ + +#define MM_MODEMMANAGER_PATH "/org/freedesktop/ModemManager" +#define MM_MODEMMANAGER_SERVICE "org.freedesktop.ModemManager" + +/************** + * Interfaces * + **************/ + + + + + + + + + + + + +#define "" + +/*********************** + * Methods/Enums/Flags * + ***********************/ + +/* + * Interface + */ + + + + + + + + + + + + + +/********** + * Errors * + **********/ + + + + + + + + + diff --git a/introspection/Makefile.am b/introspection/Makefile.am index 941c924..3c7a380 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -1,18 +1,19 @@ EXTRA_DIST = \ all.xml \ - mm-manager.xml \ + org.freedesktop.ModemManager.xml \ mm-mobile-error.xml \ - mm-modem.xml \ - mm-modem-cdma.xml \ + org.freedesktop.ModemManager.Modem.xml \ + org.freedesktop.ModemManager.Modem.Cdma.xml \ mm-modem-connect-error.xml \ mm-modem-error.xml \ - mm-modem-gsm.xml \ - mm-modem-gsm-card.xml \ - mm-modem-gsm-contacts.xml \ - mm-modem-gsm-hso.xml \ - mm-modem-gsm-network.xml \ - mm-modem-gsm-sms.xml \ - mm-modem-simple.xml \ + org.freedesktop.ModemManager.Modem.Gsm.xml \ + org.freedesktop.ModemManager.Modem.Gsm.Card.xml \ + org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml \ + org.freedesktop.ModemManager.Modem.Gsm.Hso.xml \ + org.freedesktop.ModemManager.Modem.Gsm.Network.xml \ + org.freedesktop.ModemManager.Modem.Gsm.SMS.xml \ + org.freedesktop.ModemManager.Modem.Simple.xml \ mm-serial-error.xml \ - mm-modem-location.xml \ - mm-modem-gsm-ussd.xml + org.freedesktop.ModemManager.Modem.Location.xml \ + org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml \ + org.freedesktop.DBus.Properties.xml diff --git a/introspection/all.xml b/introspection/all.xml index 967e90d..ff174d8 100644 --- a/introspection/all.xml +++ b/introspection/all.xml @@ -3,8 +3,9 @@ xmlns:xi="http://www.w3.org/2001/XInclude"> ModemManager D-Bus Interface Specification - 0.1 + 0.5 Copyright (C) 2008 Novell, Inc. + Copyright (C) 2008 - 2010 Red Hat, Inc.

This program is free software; you can redistribute it and/or modify @@ -22,18 +23,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

- - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/introspection/mm-manager.xml b/introspection/mm-manager.xml deleted file mode 100644 index bdeac01..0000000 --- a/introspection/mm-manager.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - Get the list of modem devices. - - - - - List of object paths of modem devices known to the system. - - - - - - - A device was added to the system. - - - - The object path of the newly added device. - - - - - - - A device was removed from the system, and is no longer available. - - - - The object path of the device that was just removed. - - - - - - diff --git a/introspection/mm-modem-cdma.xml b/introspection/mm-modem-cdma.xml deleted file mode 100644 index e224296..0000000 --- a/introspection/mm-modem-cdma.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - Get the current signal quality. - - - - - - Signal quality (percent). - - - - - - - Get the Electronic Serial Number of the card. - - - - - - The ESN. - - - - - - - Get the Service System details of the current network, if registered. - - - - - - A structure containing the Band Class (0 = unknown, 1 = 800 MHz, 2 = 1900 MHz), the Band ("A" - "F" as defined by IS707-A), and the System ID of the serving network. - - - - - - - The signal quality changed. - - - - The new quality in percent, 0..100. - - - - - - Get device registration state. - - - - CDMA 1x registration state. - - - EVDO registration state. - - - - - - The device registration state changed. - - - CDMA 1x registration state. - - - EVDO registration state. - - - - - - Registration status is unknown or the device is not registered. - - - Registered, but roaming status is unknown or cannot be provided by the device. The device may or may not be roaming. - - - Currently registered on the home network. - - - Currently registered on a roaming network. - - - - - - diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml deleted file mode 100644 index 9c9fdc1..0000000 --- a/introspection/mm-modem-gsm-card.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - Get the IMEI of the card. - - - - - - The IMEI. - - - - - - - Get the IMSI of the SIM card. - - - - - - The IMSI. - - - - - - - Returns the ID of the network operator that issued the SIM card, - formatted as a 5 or 6-digit MCC/MNC code (ex "310410"). - - - - - - The operator ID formatted as an MCC/MNC code. - - - - - - - Send the PUK and a new PIN to unlock the SIM card. - - - - - - The PUK code. - - - - - The PIN code. - - - - - - - Send the PIN (or PUK) to unlock the SIM card. - - - - - - The PIN code. - - - - - - - Enable or disable the PIN checking. - - - - - - The PIN code. - - - - - True to enable PIN checking. - - - - - - - Change the PIN code. - - - - - - The current PIN code. - - - - - The new PIN code. - - - - - - - Bands supported by the card. (Note for plugin writers: - returned value must not contain ANY) - - - - - - Network selection modes supported by the card. (Note for plugin writers: - returned value must not contain ANY) - - - - - diff --git a/introspection/mm-modem-gsm-contacts.xml b/introspection/mm-modem-gsm-contacts.xml deleted file mode 100644 index 60b06ad..0000000 --- a/introspection/mm-modem-gsm-contacts.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - Add a new contact to the SIM card. - - - - - - The name of the contact. - - - - - The phone number of the contact. - - - - - The index of the new contact. - - - - - - - Delete a contact from the SIM card. - - - - - - The index of the contact. - - - - - - - Retrieve a contact from the SIM card. - - - - - - The index of the contact. - - - - - The contact structure containing index, name, and number. - - - - - - - List all contacts on the SIM card. - - - - - - The list of contacts where each contact has an index, name, and number. - - - - - - - Find a contact from the SIM card. - - - - - - The pattern to search for. - - - - - The list of matching contacts where a contact has an index, name, and number. - - - - - - - Get the number of contacts stored on the SIM card. - - - - - - The number of contacts. - - - - - - diff --git a/introspection/mm-modem-gsm-hso.xml b/introspection/mm-modem-gsm-hso.xml deleted file mode 100644 index d646acc..0000000 --- a/introspection/mm-modem-gsm-hso.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - Authenticate using the passed user name and password. - - - - - The user name. - - - The password. - - - - diff --git a/introspection/mm-modem-gsm-network.xml b/introspection/mm-modem-gsm-network.xml deleted file mode 100644 index 7c26681..0000000 --- a/introspection/mm-modem-gsm-network.xml +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - Register the device to network. - - - - - - The network ID to register. An empty string can be used to register to the home network. - - - - - - - Scan for available networks. - - - - - -

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:

-
    -
  • - "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. -
  • -
  • - "operator-long": long-format name of operator. If the name is - unknown, this field should not be present. -
  • -
  • - "operator-short": short-format name of operator. If the name is - unknown, this field should not be present. -
  • -
  • - "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". -
  • -
  • - "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. -
  • -
-
-
-
- - - - Set the APN. - - - - - - The APN. - - - - - - - Get the current signal quality. - - - - - - Signal quality (percent). - - - - - - - Sets the band the device is allowed to use when connecting to a mobile network. - - - - - - The desired band. Only one band may be specified, and may not be UNKNOWN. - - - - - - - Returns the current band the device is using. (Note for plugin writers: returned value must not be ANY) - - - - - - The current band. - - - - - - - Set the desired mode the device may use when connecting to a mobile - network (DEPRECATED; see SetAllowedMode instead). - - - - - - The desired network mode. Only one mode may be specified, and may not be UNKNOWN. - - - - - - - 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). - - - - - - Returns the general network mode (ex. 2G/3G preference) of the device. - - - - - - - Get the registration status and the current operator (if registered). - - - - - - The returned information is composed of the following items in the - following order: -
    -
  • - 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. -
  • -
  • - 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". -
  • -
  • - 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. -
  • -
-
-
-
- - - - Set the access technologies a device is allowed to use when connecting - to a mobile network. - - - - - - 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. - - - - - - - The allowed access technologies (eg 2G/3G preference) the device is allowed - to use when connecting to a mobile network. - - - - - - 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) - - - - - - The signal quality changed. - - - - The new quality in percent, 0..100. - - - - - - - The registration status changed. - - - - Mobile registration status as defined in 3GPP TS 27.007 section - 10.1.19. - - - - - 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". - - - - - 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. - - - - - - - The network mode preference changed. (DEPRECATED; see documentation - for GetNetworkMode/SetNetworkMode) - - - The new network mode. - - - - - - GSM registration code as defined in 3GPP TS 27.007 section 10.1.19. - - - - Not registered, not searching for new operator to register. - - - - - Registered on home network. - - - - - Not registered, searching for new operator to register with. - - - - - Registration denied. - - - - - Unknown registration status. - - - - - Registered on a roaming network. - - - - - - - DEPRECATED; should not be used in new applications. Use - AccessTechnology, AllowedMode, and SetAllowedMode() instead. - - - Any network mode can be used - - - GPRS - - - EDGE - - - UMTS (3G) - - - HSDPA - - - Prefer 2G (GPRS or EDGE) - - - Prefer 3G (UMTS/HSDPA/HSUPA/HSPA) - - - Use only 2G (GPRS or EDGE) - - - Use only 3G (UMTS/HSDPA/HSUPA/HSPA) - - - HSUPA - - - HSPA (HSDPA + HSUPA) - - - -
-
diff --git a/introspection/mm-modem-gsm-sms.xml b/introspection/mm-modem-gsm-sms.xml deleted file mode 100644 index 081ecc5..0000000 --- a/introspection/mm-modem-gsm-sms.xml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - Delete an SMS message. - - - - - - The index of the SMS. - - - - - - - Retrieve an SMS from the SIM card. - - - - - - The index of the SMS. - - - - - A dictionary containing SMS properties of the SMS specified by the given index. This dictionary may contain the following key/value pairs: - - number : string - Phone number (mandatory) - text : string - SMS text (mandatory) - smsc : string - SMS service center number (optional) - validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) - class : uint (0..3) - Message importance and location (optional) - completed: boolean - Whether all message parts have been received or not (optional) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SMS properties to save with the following key values: - - number : string - Phone number (mandatory) - text : string - SMS text (mandatory) - smsc : string - SMS service center number (optional) - validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) - class : uint (0..3) - Message importance and location (optional) - - - - - - - - - - - SMS properties to save with the following key values: - - number : string - Phone number (mandatory) - text : string - SMS text (mandatory) - smsc : string - SMS service center number (optional) - validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) - class : uint (0..3) - Message importance and location (optional) - - - - - - - - - - - - - - - - - - - - - - - - Emitted when any part of a new SMS has been received (but not for subsequent parts, if any). Not all parts may have been received and the message may not be complete; if it is, the 'complete' argument will be TRUE. - - - - Index of the new SMS. - - - - - TRUE if all message parts have been received, otherwise FALSE. - - - - - - - Emitted when the complete-ness status of an SMS message changes. An SMS may not necessarily be complete when the first part is received; this signal will be emitted when all parts have been received, even for single-part messages. - - - - The index of the SMS. - - - - - TRUE if all message parts have been received, otherwise FALSE. - - - - - - diff --git a/introspection/mm-modem-gsm-ussd.xml b/introspection/mm-modem-gsm-ussd.xml deleted file mode 100644 index ae6884f..0000000 --- a/introspection/mm-modem-gsm-ussd.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - 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. - - - - - - The command to start the USSD session with. - - - - - The network response to the command which started the USSD session. - - - - - - - Respond to a USSD request that is either initiated by the mobile network, - or that is awaiting further input after Initiate() was called. - - - - - - The response to network-initiated USSD command, or a response to a - request for further input. - - - - - - - Cancel an ongoing USSD session, either mobile or network initiated. - - - - - - - - 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()). - - - - - - 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. - - - - - - 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. - - - - - diff --git a/introspection/mm-modem-gsm.xml b/introspection/mm-modem-gsm.xml deleted file mode 100644 index ea23adc..0000000 --- a/introspection/mm-modem-gsm.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - 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. - - - Unknown or invalid mode. - - - For certain operations, allow the modem to pick any available mode. - - - GPRS - - - EDGE - - - UMTS (3G) - - - HSDPA (3G) - - - Prefer 2G (GPRS or EDGE) - - - Prefer 3G (UMTS/HSDPA/HSUPA/HSPA) - - - Use only 2G (GPRS or EDGE) - - - Use only 3G (UMTS/HSDPA/HSUPA/HSPA) - - - HSUPA (3G) - - - HSPA (3G) - - - GSM - - - GSM Compact - - - - - - 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. - - - Unknown or invalid band - - - For certain operations, allow the modem to select a band automatically. - - - GSM/GPRS/EDGE 900 MHz - - - GSM/GPRS/EDGE 1800 MHz - - - GSM/GPRS/EDGE 1900 MHz - - - GSM/GPRS/EDGE 850 MHz - - - WCDMA 2100 MHz (Class I) - - - WCDMA 3GPP 1800 MHz (Class III) - - - WCDMA 3GPP AWS 1700/2100 MHz (Class IV) - - - WCDMA 3GPP UMTS 800 MHz (Class VI) - - - WCDMA 3GPP UMTS 850 MHz (Class V) - - - WCDMA 3GPP UMTS 900 MHz (Class VIII) - - - WCDMA 3GPP UMTS 1700 MHz (Class IX) - - - WCDMA 3GPP UMTS 1900 MHz (Class II) - - - - - - 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. - - - Any mode can be used - - - Prefer 2G (GPRS or EDGE) - - - Prefer 3G (UMTS or HSxPA) - - - Use only 2G (GPRS or EDGE) - - - Use only 3G (UMTS or HSxPA) - - - - - - Describes various access technologies that a device uses when connected - to a mobile network. - - - The access technology used is unknown - - - GSM - - - Compact GSM - - - GPRS - - - EDGE (ETSI 27.007: "GSM w/EGPRS") - - - UMTS (ETSI 27.007: "UTRAN") - - - HSDPA (ETSI 27.007: "UTRAN w/HSDPA") - - - HSUPA (ETSI 27.007: "UTRAN w/HSUPA") - - - HSPA (ETSI 27.007: "UTRAN w/HSDPA and HSUPA") - - - - - - diff --git a/introspection/mm-modem-location.xml b/introspection/mm-modem-location.xml deleted file mode 100644 index 58dca68..0000000 --- a/introspection/mm-modem-location.xml +++ /dev/null @@ -1,253 +0,0 @@ - - - - - - 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. - - - - - Location capabilities of the device. - - - - - - TRUE if location information gathering is enabled for this device, FALSE - if it is disabled. When disabled, the device will not provide location - information. - - - - - - 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. - - - - - - TRUE to enable location information gathering, FALSE to disable. - - - - - 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. - - - - - - - Return current location information, if any. This method may require - the client to authenticate itself. - - - - - - 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. - - - - - - - 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. - - - - - - 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. - - - - - - A mapping from location type to type-specific location information. - - - - 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. - - - - - Contains type-specific location information. See the documentation for - each type for a description of its data format. - - - - - - -

Unknown or no capabilties.

-
- - -

For capability reporting, indicates the device is capable of - providing GPS NMEA-format location information.

- -

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 (<CR><LF>) sequence. -

-

- 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 <CR><LF> is replaced with the actual - ASCII CR (0x0D) and LF (0x0A) control characters): -

-          $GPRMC,134523.92,V,,,,,,,030136,,,N*73<CR><LF>$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45
-          
- 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: -
-          $GPRMC,134526.92,V,,,,,,,030136,,,N*76<CR><LF>$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45
-          
- 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: -
-          $GPRMC,134526.92,V,,,,,,,030136,,,N*76<CR><LF>$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45<CR><LF>$GPGSA,A,1,,,,,,,,,,,,,1.1,0.5,1.0*34
-          
- The manager may discard any cached sentences older than 30 seconds. -

-

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. -

-
-
- - -

For capability reporting, indicates the device is capable of - providing GSM Location Area Code/Cell ID location information.

- -

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:

-
    -
  • - 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" -
  • -
  • - 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" -
  • -
  • - 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". -
  • -
  • - 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". -
  • -
-

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".

- -

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.

-
-
- - -

For capability reporting, indicates the device is capable of - providing raw GPS information using a series of defined key/value - pairs.

- -

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:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyValue TypeValue contentsExample
latitudedLatitude in Decimal Degrees (positive numbers mean N quadrasphere, negative mean S quadrasphere)38.889722 (ie, 38d 53' 22" N)
longitudedLongitude in Decimal Degrees (positive numbers mean E quadrasphere, negative mean W quadrasphere)-77.008889 (ie, 77d 0' 32" W)
altitudedAltitude above sea level in meters33.5
horiz-velocitydHorizontal velocity in meters-per-second.5
vert-velocitydVertical velocity in meters-per-second.01
-

The 'latitude' and 'longitude' keys are required; other keys are - optional.

-
-
-
- -
-
- diff --git a/introspection/mm-modem-simple.xml b/introspection/mm-modem-simple.xml deleted file mode 100644 index bee1017..0000000 --- a/introspection/mm-modem-simple.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - Do everything needed to connect the modem. - - - - - - Dictionary of properties needed to get the modem connected. - Each implementation is free to add it's own specific key-value pairs. The predefined - common ones are: - - 'pin' : string - 'network_id' : string - 'band' : uint - 'network_mode' : uint - 'apn' : string - 'number' : string - - - - - - - Get the modem status. - - - - - - Dictionary of properties. - Each implementation is free to add it's own specific key-value pairs. The predefined - common ones are: - - 'state' : uint (always) - 'signal_quality' : uint (state >= registered) - 'operator_code' : string (state >= registered) - 'operator_name' : string (state >= registered) - 'band' : uint (state >= registered) - 'network_mode' : uint (state >= registered) - - - - - diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml deleted file mode 100644 index 7d54dd3..0000000 --- a/introspection/mm-modem.xml +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - One or more properties' values changed. - - - - The D-Bus interface of the changed properties. - - - - - The changed property names and their new values. - - - - - - - - - Enable the device. Initializes the modem. - - - - - - True to enable the device, False to disable. - - - - - - - Dial in. - - - - - - The number to use for dialing. - - - - - - - Disconnect modem. - - - - - - - - Request the IP4 configuration from the device. - Note that it'll only be supported for IPMethod MM_MODEM_IP_METHOD_STATIC. - - - - - Structure containing IP4 address, DNS1, DNS2, DNS3. - The DNS list is padded with 0's if there's less than 3 DNS servers. - - - - - - Get the card information (manufacturer, modem, version). - - - - - - Structure containing manufacturer, model, and version (revision) of the card. - - - - - - - Reset the modem to as close to factory state as possible. - - - - - Carrier-supplied code required to reset the modem. Ignored if not required. - - - - - - The modem port to use for IP configuration and traffic. - - - - - - The physical modem device reference (ie, USB, PCI, PCMCIA device), which - may be dependent upon the operating system. In Linux for example, this - points to a sysfs path of the usb_device object. - - - - - - The driver handling the device. - - - - - - The modem type. - - - - - - TRUE if the modem is enabled (ie, powered and usable), FALSE if it is disabled. - - - - - - The identity of the device. This will be the IMEI number for - GSM devices and the hex-format ESN/MEID for CDMA devices. - - - - - - 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". - - - - - - 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. - - - - - - The IP configuration method. - - - - - - - A GSM device. - - - - - A CDMA device. - - - - - - - - Use PPP to get the address. - - - - - Static configuration, the modem will provide IP information. - - - - - Use DHCP - - - - - - diff --git a/introspection/org.freedesktop.DBus.Properties.xml b/introspection/org.freedesktop.DBus.Properties.xml new file mode 100644 index 0000000..b2d0923 --- /dev/null +++ b/introspection/org.freedesktop.DBus.Properties.xml @@ -0,0 +1,45 @@ + + + + + + + One or more properties' values changed. + + + + The D-Bus interface of the changed properties. + + + + + The changed property names and their new values. + + + + + + + One or more properties value changed; this signal implements the + D-Bus specification's PropertiesChanged signal. + + + + The D-Bus interface of the changed properties. + + + + + The changed property names and their new values. + + + + + Properties which are now invalid, but for which the new value is not + emitted in this signal. Clients interested in these properties should + issue a Get request for them to retrieve the new value. + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Cdma.xml b/introspection/org.freedesktop.ModemManager.Modem.Cdma.xml new file mode 100644 index 0000000..d80d9b9 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Cdma.xml @@ -0,0 +1,220 @@ + + + + + + + + Activates the modem for use with a given carrier. In the + event of immediate failure, returns an error value instead of + setting a DBus error. + + + + + + Name of carrier. + + + + + An enum from MM_MODEM_CDMA_ACTIVATION_ERROR. This is + returned for immediate errors. Delayed errors are returned + via an ActivationStateChanged signal + + + + + + + Sets modem configuration data. Unlike regular Activate(), + this does not contact the carrier. Some modems will reboot + after this call is made. + + + + + + A dictionary of properties to set on the modem. Keys include 'mdn', 'min' + + + + + + + The device activation state changed. + + + Current activation state + + + Carrier-specific error code + + + Selected Modem.Simple.GetStatus keys that have changed as a + result of this activation state change. Will include 'mdn' + and 'min'. + + + + + + + Get the current signal quality. + + + + + + Signal quality (percent). + + + + + + + Get the Electronic Serial Number of the card. + + + + + + The ESN. + + + + + + + Get the Service System details of the current network, if registered. + + + + + + A structure containing the Band Class (0 = unknown, 1 = 800 MHz, 2 = 1900 MHz), the Band ("A" - "F" as defined by IS707-A), and the System ID of the serving network. + + + + + + + The signal quality changed. + + + + The new quality in percent, 0..100. + + + + + + Get device registration state. + + + + CDMA 1x registration state. + + + EVDO registration state. + + + + + + The modem's Mobile Equipment Identifier. + + + + + + The device registration state changed. + + + CDMA 1x registration state. + + + EVDO registration state. + + + + + + Registration status is unknown or the device is not registered. + + + Registered, but roaming status is unknown or cannot be provided by the device. The device may or may not be roaming. + + + Currently registered on the home network. + + + Currently registered on a roaming network. + + + + + + Device is not activated + + + Device is activating + + + Device is partially activated; carrier-specific steps required to continue. + + + Device is ready for use. + + + + + + + + Device cannot activate while roaming. + + + + + Device cannot activate on this network type (eg EVDO vs 1xRTT). + + + + + Device could not connect to the network for activation. + + + + + Device could not authenticate to the network for activation. + + + + + Later stages of device provisioning failed. + + + + + No signal available. + + + + + An error occurred. + + + + + Activation timed out. + + + + + API call for initial activation failed. + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml new file mode 100644 index 0000000..d481157 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml @@ -0,0 +1,135 @@ + + + + + + + Get the IMEI of the card. + + + + + + The IMEI. + + + + + + + Get the IMSI of the SIM card. + + + + + + The IMSI. + + + + + + + Returns the ID of the network operator that issued the SIM card, + formatted as a 5 or 6-digit MCC/MNC code (ex "310410"). + + + + + + The operator ID formatted as an MCC/MNC code. + + + + + + + Send the PUK and a new PIN to unlock the SIM card. + + + + + + The PUK code. + + + + + The PIN code. + + + + + + + Send the PIN (or PUK) to unlock the SIM card. + + + + + + The PIN code. + + + + + + + Enable or disable the PIN checking. + + + + + + The PIN code. + + + + + True to enable PIN checking. + + + + + + + Change the PIN code. + + + + + + The current PIN code. + + + + + The new PIN code. + + + + + + + An obfuscated SIM identifier based on the IMSI or the ICCID. This may + be available before the PIN has been entered depending on the device + itself. + + + + + + Bands supported by the card. (Note for plugin writers: + returned value must not contain ANY) + + + + + + Network selection modes supported by the card. (Note for plugin writers: + returned value must not contain ANY) + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml new file mode 100644 index 0000000..60b06ad --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml @@ -0,0 +1,104 @@ + + + + + + + Add a new contact to the SIM card. + + + + + + The name of the contact. + + + + + The phone number of the contact. + + + + + The index of the new contact. + + + + + + + Delete a contact from the SIM card. + + + + + + The index of the contact. + + + + + + + Retrieve a contact from the SIM card. + + + + + + The index of the contact. + + + + + The contact structure containing index, name, and number. + + + + + + + List all contacts on the SIM card. + + + + + + The list of contacts where each contact has an index, name, and number. + + + + + + + Find a contact from the SIM card. + + + + + + The pattern to search for. + + + + + The list of matching contacts where a contact has an index, name, and number. + + + + + + + Get the number of contacts stored on the SIM card. + + + + + + The number of contacts. + + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml new file mode 100644 index 0000000..d646acc --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml @@ -0,0 +1,19 @@ + + + + + + + Authenticate using the passed user name and password. + + + + + The user name. + + + The password. + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml new file mode 100644 index 0000000..7c26681 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml @@ -0,0 +1,340 @@ + + + + + + + Register the device to network. + + + + + + The network ID to register. An empty string can be used to register to the home network. + + + + + + + Scan for available networks. + + + + + +

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:

+
    +
  • + "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. +
  • +
  • + "operator-long": long-format name of operator. If the name is + unknown, this field should not be present. +
  • +
  • + "operator-short": short-format name of operator. If the name is + unknown, this field should not be present. +
  • +
  • + "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". +
  • +
  • + "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. +
  • +
+
+
+
+ + + + Set the APN. + + + + + + The APN. + + + + + + + Get the current signal quality. + + + + + + Signal quality (percent). + + + + + + + Sets the band the device is allowed to use when connecting to a mobile network. + + + + + + The desired band. Only one band may be specified, and may not be UNKNOWN. + + + + + + + Returns the current band the device is using. (Note for plugin writers: returned value must not be ANY) + + + + + + The current band. + + + + + + + Set the desired mode the device may use when connecting to a mobile + network (DEPRECATED; see SetAllowedMode instead). + + + + + + The desired network mode. Only one mode may be specified, and may not be UNKNOWN. + + + + + + + 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). + + + + + + Returns the general network mode (ex. 2G/3G preference) of the device. + + + + + + + Get the registration status and the current operator (if registered). + + + + + + The returned information is composed of the following items in the + following order: +
    +
  • + 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. +
  • +
  • + 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". +
  • +
  • + 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. +
  • +
+
+
+
+ + + + Set the access technologies a device is allowed to use when connecting + to a mobile network. + + + + + + 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. + + + + + + + The allowed access technologies (eg 2G/3G preference) the device is allowed + to use when connecting to a mobile network. + + + + + + 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) + + + + + + The signal quality changed. + + + + The new quality in percent, 0..100. + + + + + + + The registration status changed. + + + + Mobile registration status as defined in 3GPP TS 27.007 section + 10.1.19. + + + + + 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". + + + + + 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. + + + + + + + The network mode preference changed. (DEPRECATED; see documentation + for GetNetworkMode/SetNetworkMode) + + + The new network mode. + + + + + + GSM registration code as defined in 3GPP TS 27.007 section 10.1.19. + + + + Not registered, not searching for new operator to register. + + + + + Registered on home network. + + + + + Not registered, searching for new operator to register with. + + + + + Registration denied. + + + + + Unknown registration status. + + + + + Registered on a roaming network. + + + + + + + DEPRECATED; should not be used in new applications. Use + AccessTechnology, AllowedMode, and SetAllowedMode() instead. + + + Any network mode can be used + + + GPRS + + + EDGE + + + UMTS (3G) + + + HSDPA + + + Prefer 2G (GPRS or EDGE) + + + Prefer 3G (UMTS/HSDPA/HSUPA/HSPA) + + + Use only 2G (GPRS or EDGE) + + + Use only 3G (UMTS/HSDPA/HSUPA/HSPA) + + + HSUPA + + + HSPA (HSDPA + HSUPA) + + + +
+
diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml new file mode 100644 index 0000000..081ecc5 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml @@ -0,0 +1,156 @@ + + + + + + + Delete an SMS message. + + + + + + The index of the SMS. + + + + + + + Retrieve an SMS from the SIM card. + + + + + + The index of the SMS. + + + + + A dictionary containing SMS properties of the SMS specified by the given index. This dictionary may contain the following key/value pairs: + + number : string - Phone number (mandatory) + text : string - SMS text (mandatory) + smsc : string - SMS service center number (optional) + validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) + class : uint (0..3) - Message importance and location (optional) + completed: boolean - Whether all message parts have been received or not (optional) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SMS properties to save with the following key values: + + number : string - Phone number (mandatory) + text : string - SMS text (mandatory) + smsc : string - SMS service center number (optional) + validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) + class : uint (0..3) - Message importance and location (optional) + + + + + + + + + + + SMS properties to save with the following key values: + + number : string - Phone number (mandatory) + text : string - SMS text (mandatory) + smsc : string - SMS service center number (optional) + validity : uint (0..255) - Specifies when the SMS expires in SMSC (optional) + class : uint (0..3) - Message importance and location (optional) + + + + + + + + + + + + + + + + + + + + + + + + Emitted when any part of a new SMS has been received (but not for subsequent parts, if any). Not all parts may have been received and the message may not be complete; if it is, the 'complete' argument will be TRUE. + + + + Index of the new SMS. + + + + + TRUE if all message parts have been received, otherwise FALSE. + + + + + + + Emitted when the complete-ness status of an SMS message changes. An SMS may not necessarily be complete when the first part is received; this signal will be emitted when all parts have been received, even for single-part messages. + + + + The index of the SMS. + + + + + TRUE if all message parts have been received, otherwise FALSE. + + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml new file mode 100644 index 0000000..8eef91d --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml @@ -0,0 +1,84 @@ + + + + + + + + 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. + + + + + + The command to start the USSD session with. + + + + + The network response to the command which started the USSD session. + + + + + + + Respond to a USSD request that is either initiated by the mobile network, + or that is awaiting further input after Initiate() was called. + + + + + + The response to network-initiated USSD command, or a response to a + request for further input. + + + + + The network reply to this response to the network-initiated USSD + command. The reply may require further responses. + + + + + + + Cancel an ongoing USSD session, either mobile or network initiated. + + + + + + + + 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()). + + + + + + 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. + + + + + + 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. + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml b/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml new file mode 100644 index 0000000..78da9a4 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Gsm.xml @@ -0,0 +1,167 @@ + + + + + + + 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. + + + Unknown or invalid mode. + + + For certain operations, allow the modem to pick any available mode. + + + GPRS + + + EDGE + + + UMTS (3G) + + + HSDPA (3G) + + + Prefer 2G (GPRS or EDGE) + + + Prefer 3G (UMTS/HSDPA/HSUPA/HSPA) + + + Use only 2G (GPRS or EDGE) + + + Use only 3G (UMTS/HSDPA/HSUPA/HSPA) + + + HSUPA (3G) + + + HSPA (3G) + + + GSM + + + GSM Compact + + + + + + 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. + + + Unknown or invalid band + + + For certain operations, allow the modem to select a band automatically. + + + GSM/GPRS/EDGE 900 MHz + + + GSM/GPRS/EDGE 1800 MHz + + + GSM/GPRS/EDGE 1900 MHz + + + GSM/GPRS/EDGE 850 MHz + + + WCDMA 2100 MHz (Class I) + + + WCDMA 3GPP 1800 MHz (Class III) + + + WCDMA 3GPP AWS 1700/2100 MHz (Class IV) + + + WCDMA 3GPP UMTS 800 MHz (Class VI) + + + WCDMA 3GPP UMTS 850 MHz (Class V) + + + WCDMA 3GPP UMTS 900 MHz (Class VIII) + + + WCDMA 3GPP UMTS 1700 MHz (Class IX) + + + WCDMA 3GPP UMTS 1900 MHz (Class II) + + + + + + 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. + + + Any mode can be used + + + Prefer 2G (GPRS or EDGE) + + + Prefer 3G (UMTS or HSxPA) + + + Use only 2G (GPRS or EDGE) + + + Use only 3G (UMTS or HSxPA) + + + + + + Describes various access technologies that a device uses when connected + to a mobile network. + + + The access technology used is unknown + + + GSM + + + Compact GSM + + + GPRS + + + EDGE (ETSI 27.007: "GSM w/EGPRS") + + + UMTS (ETSI 27.007: "UTRAN") + + + HSDPA (ETSI 27.007: "UTRAN w/HSDPA") + + + HSUPA (ETSI 27.007: "UTRAN w/HSUPA") + + + HSPA (ETSI 27.007: "UTRAN w/HSDPA and HSUPA") + + + HSPA+ (ETSI 27.007: "UTRAN w/HSPA+") + + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.Location.xml b/introspection/org.freedesktop.ModemManager.Modem.Location.xml new file mode 100644 index 0000000..d74f61c --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Location.xml @@ -0,0 +1,253 @@ + + + + + + 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. + + + + + Location capabilities of the device. + + + + + + TRUE if location information gathering is enabled for this device, FALSE + if it is disabled. When disabled, the device will not provide location + information. + + + + + + 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. + + + + + + TRUE to enable location information gathering, FALSE to disable. + + + + + 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. + + + + + + + Return current location information, if any. This method may require + the client to authenticate itself. + + + + + + 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. + + + + + + + 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. + + + + + + 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. + + + + + + A mapping from location type to type-specific location information. + + + + 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. + + + + + Contains type-specific location information. See the documentation for + each type for a description of its data format. + + + + + + +

Unknown or no capabilties.

+
+ + +

For capability reporting, indicates the device is capable of + providing GPS NMEA-format location information.

+ +

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 (<CR><LF>) sequence. +

+

+ 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 <CR><LF> is replaced with the actual + ASCII CR (0x0D) and LF (0x0A) control characters): +

+          $GPRMC,134523.92,V,,,,,,,030136,,,N*73<CR><LF>$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45
+          
+ 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: +
+          $GPRMC,134526.92,V,,,,,,,030136,,,N*76<CR><LF>$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45
+          
+ 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: +
+          $GPRMC,134526.92,V,,,,,,,030136,,,N*76<CR><LF>$GPGGA,,,,,,0,00,0.5,,M,0.0001999,M,0.0000099,0000*45<CR><LF>$GPGSA,A,1,,,,,,,,,,,,,1.1,0.5,1.0*34
+          
+ The manager may discard any cached sentences older than 30 seconds. +

+

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. +

+
+
+ + +

For capability reporting, indicates the device is capable of + providing GSM Location Area Code/Cell ID location information.

+ +

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:

+
    +
  • + 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" +
  • +
  • + 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" +
  • +
  • + 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". +
  • +
  • + 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". +
  • +
+

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".

+ +

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.

+
+
+ + +

For capability reporting, indicates the device is capable of + providing raw GPS information using a series of defined key/value + pairs.

+ +

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:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyValue TypeValue contentsExample
latitudedLatitude in Decimal Degrees (positive numbers mean N quadrasphere, negative mean S quadrasphere)38.889722 (ie, 38d 53' 22" N)
longitudedLongitude in Decimal Degrees (positive numbers mean E quadrasphere, negative mean W quadrasphere)-77.008889 (ie, 77d 0' 32" W)
altitudedAltitude above sea level in meters33.5
horiz-velocitydHorizontal velocity in meters-per-second.5
vert-velocitydVertical velocity in meters-per-second.01
+

The 'latitude' and 'longitude' keys are required; other keys are + optional.

+
+
+
+ +
+
+ diff --git a/introspection/org.freedesktop.ModemManager.Modem.Simple.xml b/introspection/org.freedesktop.ModemManager.Modem.Simple.xml new file mode 100644 index 0000000..bee1017 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.Simple.xml @@ -0,0 +1,58 @@ + + + + + + + + Do everything needed to connect the modem. + + + + + + Dictionary of properties needed to get the modem connected. + Each implementation is free to add it's own specific key-value pairs. The predefined + common ones are: + + 'pin' : string + 'network_id' : string + 'band' : uint + 'network_mode' : uint + 'apn' : string + 'number' : string + + + + + + + Get the modem status. + + + + + + Dictionary of properties. + Each implementation is free to add it's own specific key-value pairs. The predefined + common ones are: + + 'state' : uint (always) + 'signal_quality' : uint (state >= registered) + 'operator_code' : string (state >= registered) + 'operator_name' : string (state >= registered) + 'band' : uint (state >= registered) + 'network_mode' : uint (state >= registered) + + + + + diff --git a/introspection/org.freedesktop.ModemManager.Modem.xml b/introspection/org.freedesktop.ModemManager.Modem.xml new file mode 100644 index 0000000..3f6f0bb --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.Modem.xml @@ -0,0 +1,245 @@ + + + + + + + + Enable the device. Initializes the modem. + + + + + + True to enable the device, False to disable. + + + + + + + Dial in. + + + + + + The number to use for dialing. + + + + + + + Disconnect modem. + + + + + + + + Request the IP4 configuration from the device. + Note that it'll only be supported for IPMethod MM_MODEM_IP_METHOD_STATIC. + + + + + Structure containing IP4 address, DNS1, DNS2, DNS3. + The DNS list is padded with 0's if there's less than 3 DNS servers. + + + + + + Get the card information (manufacturer, modem, version). + + + + + + Structure containing manufacturer, model, and version (revision) of the card. + + + + + + + Clear non-persistent configuration and state, and return the device to + a newly-powered-on state. This command may power-cycle the device. + + + + + + + + Clear the modem's configuration (including persistent configuration and + state), and return the device to a factory-default state. This command + may or may not power-cycle the device. + + + + + Carrier-supplied code required to reset the modem. Ignored if not required. + + + + + + The modem's state (see the State property) changed. + + + + Old state. + + + + + New state. + + + + + Reason for this state change. + + + + + + + The modem port to use for IP configuration and traffic. + + + + + + A best-effort device identifier based on various device information like + model name, firmware revision, USB/PCI/PCMCIA IDs, and other properties. + This ID is not guaranteed to be unique and may be shared between + identical devices with the same firmware, but is intended to be + "unique enough" for use as a casual device identifier for various + user experience operations. This is not the device's IMEI or ESN since + those may not be available before unlocking the device via a PIN. + + + + + + The physical modem device reference (ie, USB, PCI, PCMCIA device), which + may be dependent upon the operating system. In Linux for example, this + points to a sysfs path of the usb_device object. + + + + + + The driver handling the device. + + + + + + The modem type. + + + + + + TRUE if the modem is enabled (ie, powered and usable), FALSE if it is disabled. + + + + + + The identity of the device. This will be the IMEI number for + GSM devices and the hex-format ESN/MEID for CDMA devices. + + + + + + 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". + + + + + + 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. + + + + + + The IP configuration method. + + + + + + + State of the modem. + + + + + + + A GSM device. + + + + + A CDMA device. + + + + + + + + Use PPP to get the address. + + + + + Static configuration, the modem will provide IP information. + + + + + Use DHCP + + + + + + + + Reason unknown or not reportable. + + + + + State change was requested by an interface user. + + + + + State change was caused by a system suspend. + + + + + + + diff --git a/introspection/org.freedesktop.ModemManager.xml b/introspection/org.freedesktop.ModemManager.xml new file mode 100644 index 0000000..bdeac01 --- /dev/null +++ b/introspection/org.freedesktop.ModemManager.xml @@ -0,0 +1,40 @@ + + + + + + + Get the list of modem devices. + + + + + List of object paths of modem devices known to the system. + + + + + + + A device was added to the system. + + + + The object path of the newly added device. + + + + + + + A device was removed from the system, and is no longer available. + + + + The object path of the device that was just removed. + + + + + + diff --git a/libqcdm/src/Makefile.am b/libqcdm/src/Makefile.am index 5cc0568..f9451c6 100644 --- a/libqcdm/src/Makefile.am +++ b/libqcdm/src/Makefile.am @@ -22,9 +22,6 @@ libqcdm_la_SOURCES = \ libqcdm_la_LIBADD = \ $(MM_LIBS) -libqcdm_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libqcdm.ver \ - -version-info "0:0:0" - ########################################### # Test library without symbol versioning diff --git a/libqcdm/src/commands.c b/libqcdm/src/commands.c index 11a1a38..ab61ee5 100644 --- a/libqcdm/src/commands.c +++ b/libqcdm/src/commands.c @@ -119,6 +119,8 @@ bin2hexstr (const guint8 *bytes, int len) return result; } +/**********************************************************************/ + static gboolean check_command (const char *buf, gsize len, guint8 cmd, gsize min_len, GError **error) { @@ -154,6 +156,11 @@ check_command (const char *buf, gsize len, guint8 cmd, gsize min_len, GError **e "DM command %d not allowed in the current device mode", cmd); return FALSE; + case DIAG_CMD_BAD_SPC_MODE: + g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_SPC_LOCKED, + "DM command %d not allowed because the Service Programming Code is locked", + cmd); + return FALSE; default: break; } @@ -430,6 +437,52 @@ qcdm_cmd_sw_version_result (const char *buf, gsize len, GError **error) /**********************************************************************/ +gsize +qcdm_cmd_status_snapshot_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_SNAPSHOT; + + return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); +} + +static guint8 +snapshot_state_to_qcdm (guint8 cdma_state) +{ + /* CDMA_STATUS_SNAPSHOT_STATE_* -> QCDM_STATUS_SNAPSHOT_STATE_* */ + return cdma_state + 1; +} + +QCDMResult * +qcdm_cmd_status_snapshot_result (const char *buf, gsize len, GError **error) +{ + QCDMResult *result = NULL; + DMCmdStatusSnapshotRsp *rsp = (DMCmdStatusSnapshotRsp *) buf; + + g_return_val_if_fail (buf != NULL, NULL); + + if (!check_command (buf, len, DIAG_CMD_STATUS_SNAPSHOT, sizeof (*rsp), error)) + return NULL; + + result = qcdm_result_new (); + + qcdm_result_add_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BAND_CLASS, cdma_band_class_to_qcdm (rsp->band_class)); + qcdm_result_add_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BASE_STATION_PREV, cdma_prev_to_qcdm (rsp->prev)); + qcdm_result_add_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_MOBILE_PREV, cdma_prev_to_qcdm (rsp->mob_prev)); + qcdm_result_add_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_PREV_IN_USE, cdma_prev_to_qcdm (rsp->prev_in_use)); + qcdm_result_add_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_STATE, snapshot_state_to_qcdm (rsp->state & 0xF)); + + return result; +} + +/**********************************************************************/ + gsize qcdm_cmd_pilot_sets_new (char *buf, gsize len, GError **error) { @@ -820,6 +873,107 @@ qcdm_cmd_nv_set_mode_pref_result (const char *buf, gsize len, GError **error) /**********************************************************************/ +static gboolean +hdr_rev_pref_validate (guint8 dm) +{ + if ( dm == DIAG_NV_HDR_REV_PREF_0 + || dm == DIAG_NV_HDR_REV_PREF_A + || dm == DIAG_NV_HDR_REV_PREF_EHRPD) + return TRUE; + return FALSE; +} + +gsize +qcdm_cmd_nv_get_hdr_rev_pref_new (char *buf, gsize len, GError **error) +{ + char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; + DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &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_NV_READ; + cmd->nv_item = GUINT16_TO_LE (DIAG_NV_HDR_REV_PREF); + + return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); +} + +QCDMResult * +qcdm_cmd_nv_get_hdr_rev_pref_result (const char *buf, gsize len, GError **error) +{ + QCDMResult *result = NULL; + DMCmdNVReadWrite *rsp = (DMCmdNVReadWrite *) buf; + DMNVItemHdrRevPref *rev; + + 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_HDR_REV_PREF, error)) + return NULL; + + rev = (DMNVItemHdrRevPref *) &rsp->data[0]; + + if (!hdr_rev_pref_validate (rev->rev_pref)) { + g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER, + "Unknown HDR revision preference 0x%X", + rev->rev_pref); + return NULL; + } + + result = qcdm_result_new (); + qcdm_result_add_uint8 (result, QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF, rev->rev_pref); + + return result; +} + +gsize +qcdm_cmd_nv_set_hdr_rev_pref_new (char *buf, + gsize len, + guint8 rev_pref, + GError **error) +{ + char cmdbuf[sizeof (DMCmdNVReadWrite) + 2]; + DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0]; + DMNVItemHdrRevPref *req; + + g_return_val_if_fail (buf != NULL, 0); + g_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0); + + if (!hdr_rev_pref_validate (rev_pref)) { + g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_PARAMETER, + "Invalid HDR revision preference %d", rev_pref); + return 0; + } + + memset (cmd, 0, sizeof (*cmd)); + cmd->code = DIAG_CMD_NV_WRITE; + cmd->nv_item = GUINT16_TO_LE (DIAG_NV_HDR_REV_PREF); + + req = (DMNVItemHdrRevPref *) &cmd->data[0]; + req->rev_pref = rev_pref; + + return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); +} + +QCDMResult * +qcdm_cmd_nv_set_hdr_rev_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_HDR_REV_PREF, error)) + return NULL; + + return qcdm_result_new (); +} + +/**********************************************************************/ + gsize qcdm_cmd_cm_subsys_state_info_new (char *buf, gsize len, GError **error) { @@ -939,6 +1093,137 @@ qcdm_cmd_hdr_subsys_state_info_result (const char *buf, gsize len, GError **erro /**********************************************************************/ +gsize +qcdm_cmd_ext_logmask_new (char *buf, + gsize len, + GSList *items, + guint16 maxlog, + GError **error) +{ + char cmdbuf[sizeof (DMCmdExtLogMask) + 2]; + DMCmdExtLogMask *cmd = (DMCmdExtLogMask *) &cmdbuf[0]; + GSList *iter; + guint16 highest = 0; + gsize total = 3; + + 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_EXT_LOGMASK; + + for (iter = items; iter; iter = g_slist_next (iter)) { + guint32 item = GPOINTER_TO_UINT (iter->data); + + g_warn_if_fail (item > 0); + g_warn_if_fail (item < 4095); + cmd->mask[item / 8] |= 1 << item % 8; + + if (item > highest) + highest = item; + } + + g_return_val_if_fail (highest <= maxlog, 0); + cmd->len = GUINT16_TO_LE (maxlog); + total += maxlog / 8; + if (maxlog && maxlog % 8) + total++; + + return dm_encapsulate_buffer (cmdbuf, total, sizeof (cmdbuf), buf, len); +} + +QCDMResult * +qcdm_cmd_ext_logmask_result (const char *buf, + gsize len, + GError **error) +{ + QCDMResult *result = NULL; + DMCmdExtLogMask *rsp = (DMCmdExtLogMask *) buf; + guint32 masklen = 0, maxlog = 0; + gsize minlen = 0; + + g_return_val_if_fail (buf != NULL, NULL); + + /* Ensure size is at least enough for the command header */ + if (len < 1) { + g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_LENGTH, + "DM command %d response not long enough (got %zu, expected " + "at least %d).", DIAG_CMD_EXT_LOGMASK, len, 3); + return FALSE; + } + + /* Result of a 'set' operation will be only 1 byte in size; result of + * a 'get' operation (ie setting len to 0x0000 in the request) will be + * the size of the header (3) plus the max log length. + */ + + if (len == 1) + minlen = 1; + else { + /* Ensure size is equal to max # of log items + 3 */ + maxlog = GUINT16_FROM_LE (rsp->len); + masklen = maxlog / 8; + if (maxlog % 8) + masklen++; + + if (len < (masklen + 3)) { + g_set_error (error, QCDM_COMMAND_ERROR, QCDM_COMMAND_BAD_LENGTH, + "DM command %d response not long enough (got %zu, expected " + "at least %d).", DIAG_CMD_EXT_LOGMASK, len, masklen + 3); + return FALSE; + } + minlen = masklen + 3; + } + + if (!check_command (buf, len, DIAG_CMD_EXT_LOGMASK, minlen, error)) + return NULL; + + result = qcdm_result_new (); + + if (minlen != 4) + qcdm_result_add_uint32 (result, QCDM_CMD_EXT_LOGMASK_ITEM_MAX_ITEMS, maxlog); + + return result; +} + +gboolean +qcmd_cmd_ext_logmask_result_get_item (QCDMResult *result, + guint16 item) +{ + return FALSE; +} + +/**********************************************************************/ + +gsize +qcdm_cmd_event_report_new (char *buf, gsize len, gboolean start, GError **error) +{ + char cmdbuf[4]; + DMCmdEventReport *cmd = (DMCmdEventReport *) &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_EVENT_REPORT; + cmd->on = start ? 1 : 0; + + return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len); +} + +QCDMResult * +qcdm_cmd_event_report_result (const char *buf, gsize len, GError **error) +{ + g_return_val_if_fail (buf != NULL, NULL); + + if (!check_command (buf, len, DIAG_CMD_EVENT_REPORT, sizeof (DMCmdEventReport), error)) + return NULL; + + return qcdm_result_new (); +} + +/**********************************************************************/ + gsize qcdm_cmd_zte_subsys_status_new (char *buf, gsize len, GError **error) { diff --git a/libqcdm/src/commands.h b/libqcdm/src/commands.h index 75e83a7..fe582a6 100644 --- a/libqcdm/src/commands.h +++ b/libqcdm/src/commands.h @@ -148,6 +148,54 @@ QCDMResult *qcdm_cmd_sw_version_result (const char *buf, /**********************************************************************/ +/* One of QCDM_CDMA_BAND_CLASS_* */ +#define QCDM_CMD_STATUS_SNAPSHOT_ITEM_BAND_CLASS "band-class" + +/* The protocol revision of the base station. One of QCDM_CDMA_PREV_* */ +#define QCDM_CMD_STATUS_SNAPSHOT_ITEM_BASE_STATION_PREV "prev" + +/* The protocol revision of the mobile terminal. One of QCDM_CDMA_PREV_* */ +#define QCDM_CMD_STATUS_SNAPSHOT_ITEM_MOBILE_PREV "mob-prev" + +/* The protocol revision currently in-use. One of QCDM_CDMA_PREV_* */ +#define QCDM_CMD_STATUS_SNAPSHOT_ITEM_PREV_IN_USE "prev-in-use" + +enum { + QCDM_CMD_STATUS_SNAPSHOT_STATE_UNKNOWN = 0x00, + QCDM_CMD_STATUS_SNAPSHOT_STATE_NO_SERVICE = 0x01, + QCDM_CMD_STATUS_SNAPSHOT_STATE_INITIALIZATION = 0x02, + QCDM_CMD_STATUS_SNAPSHOT_STATE_IDLE = 0x03, + QCDM_CMD_STATUS_SNAPSHOT_STATE_VOICE_CHANNEL_INIT = 0x04, + QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ORDER = 0x05, + QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ANSWER = 0x06, + QCDM_CMD_STATUS_SNAPSHOT_STATE_CONVERSATION = 0x07, + QCDM_CMD_STATUS_SNAPSHOT_STATE_RELEASE = 0x08, + QCDM_CMD_STATUS_SNAPSHOT_STATE_SYSTEM_ACCESS = 0x09, + QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_CDMA = 0x11, + QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_HDR = 0x12, + QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_ANALOG = 0x13, + QCDM_CMD_STATUS_SNAPSHOT_STATE_RESET = 0x14, + QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_DOWN = 0x15, + QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_SAVE = 0x16, + QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_UP = 0x17, + QCDM_CMD_STATUS_SNAPSHOT_STATE_LOW_POWER_MODE = 0x18, + QCDM_CMD_STATUS_SNAPSHOT_STATE_SEARCHER_DSMM = 0x19, /* Dedicated System Measurement Mode */ + QCDM_CMD_STATUS_SNAPSHOT_STATE_HDR = 0x41, +}; + +/* The protocol revision currently in-use. One of QCDM_STATUS_SNAPSHOT_STATE_* */ +#define QCDM_CMD_STATUS_SNAPSHOT_ITEM_STATE "state" + +gsize qcdm_cmd_status_snapshot_new (char *buf, + gsize len, + GError **error); + +QCDMResult *qcdm_cmd_status_snapshot_result (const char *buf, + gsize len, + GError **error); + +/**********************************************************************/ + enum { QCDM_CMD_PILOT_SETS_TYPE_UNKNOWN = 0, QCDM_CMD_PILOT_SETS_TYPE_ACTIVE = 1, @@ -252,6 +300,34 @@ QCDMResult *qcdm_cmd_nv_set_mode_pref_result (const char *buf, /**********************************************************************/ +/* Values for QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF */ +enum { + QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_0 = 0x00, + QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A = 0x01, + QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD = 0x04, +}; + +#define QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF "rev-pref" + +gsize qcdm_cmd_nv_get_hdr_rev_pref_new (char *buf, + gsize len, + GError **error); + +QCDMResult *qcdm_cmd_nv_get_hdr_rev_pref_result (const char *buf, + gsize len, + GError **error); + +gsize qcdm_cmd_nv_set_hdr_rev_pref_new (char *buf, + gsize len, + guint8 rev_pref, + GError **error); + +QCDMResult *qcdm_cmd_nv_set_hdr_rev_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 @@ -265,7 +341,10 @@ enum { 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 + QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GPS = 6, + QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GW = 7, /* GSM & WCDMA */ + QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WLAN = 8, + QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_LTE = 9, }; /* Values for QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF */ @@ -392,6 +471,36 @@ QCDMResult *qcdm_cmd_hdr_subsys_state_info_result (const char *buf, /**********************************************************************/ +/* Max # of log items this device supports */ +#define QCDM_CMD_EXT_LOGMASK_ITEM_MAX_ITEMS "max-items" + +gsize qcdm_cmd_ext_logmask_new (char *buf, + gsize len, + GSList *items, + guint16 maxlog, + GError **error); + +QCDMResult *qcdm_cmd_ext_logmask_result (const char *buf, + gsize len, + GError **error); + +/* Returns TRUE if 'item' is set in the log mask */ +gboolean qcmd_cmd_ext_logmask_result_get_item (QCDMResult *result, + guint16 item); + +/**********************************************************************/ + +gsize qcdm_cmd_event_report_new (char *buf, + gsize len, + gboolean start, + GError **error); + +QCDMResult *qcdm_cmd_event_report_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, diff --git a/libqcdm/src/dm-commands.h b/libqcdm/src/dm-commands.h index cfc98f7..6ce3c5d 100644 --- a/libqcdm/src/dm-commands.h +++ b/libqcdm/src/dm-commands.h @@ -180,6 +180,28 @@ enum { CDMA_BAND_CLASS_12_PAMR_800 = 12 }; +enum { + CDMA_STATUS_SNAPSHOT_STATE_NO_SERVICE = 0x00, + CDMA_STATUS_SNAPSHOT_STATE_INITIALIZATION = 0x01, + CDMA_STATUS_SNAPSHOT_STATE_IDLE = 0x02, + CDMA_STATUS_SNAPSHOT_STATE_VOICE_CHANNEL_INIT = 0x03, + CDMA_STATUS_SNAPSHOT_STATE_WAITING_FOR_ORDER = 0x04, + CDMA_STATUS_SNAPSHOT_STATE_WAITING_FOR_ANSWER = 0x05, + CDMA_STATUS_SNAPSHOT_STATE_CONVERSATION = 0x06, + CDMA_STATUS_SNAPSHOT_STATE_RELEASE = 0x07, + CDMA_STATUS_SNAPSHOT_STATE_SYSTEM_ACCESS = 0x08, + CDMA_STATUS_SNAPSHOT_STATE_OFFLINE_CDMA = 0x10, + CDMA_STATUS_SNAPSHOT_STATE_OFFLINE_HDR = 0x11, + CDMA_STATUS_SNAPSHOT_STATE_OFFLINE_ANALOG = 0x12, + CDMA_STATUS_SNAPSHOT_STATE_RESET = 0x13, + CDMA_STATUS_SNAPSHOT_STATE_POWER_DOWN = 0x14, + CDMA_STATUS_SNAPSHOT_STATE_POWER_SAVE = 0x15, + CDMA_STATUS_SNAPSHOT_STATE_POWER_UP = 0x16, + CDMA_STATUS_SNAPSHOT_STATE_LOW_POWER_MODE = 0x17, + CDMA_STATUS_SNAPSHOT_STATE_SEARCHER_DSMM = 0x18, /* Dedicated System Measurement Mode */ + CDMA_STATUS_SNAPSHOT_STATE_HDR = 0x40, +}; + /* Generic DM command header */ struct DMCmdHeader { guint8 code; @@ -265,6 +287,29 @@ struct DMCmdSwVersionRsp { } __attribute__ ((packed)); typedef struct DMCmdSwVersionRsp DMCmdSwVersionRsp; +/* DIAG_CMD_STATUS_SNAPSHOT */ +struct DMCmdStatusSnapshotRsp { + guint8 code; + guint8 esn[4]; + guint8 imsi_s1[4]; + guint8 imsi_s2[2]; + guint8 imsi_s[8]; + guint8 imsi_11_12; + guint16 mcc; + guint8 imsi_addr_num; + guint16 sid; + guint16 nid; + guint8 prev; + guint8 prev_in_use; + guint8 mob_prev; + guint8 band_class; + guint16 frequency; + guint8 oper_mode; + guint8 state; + guint8 sub_state; +} __attribute__ ((packed)); +typedef struct DMCmdStatusSnapshotRsp DMCmdStatusSnapshotRsp; + /* DIAG_SUBSYS_CM_STATE_INFO subsys command */ struct DMCmdSubsysCMStateInfoRsp { DMCmdSubsysHeader header; @@ -323,6 +368,29 @@ struct DMCmdPilotSetsRsp { } __attribute__ ((packed)); typedef struct DMCmdPilotSetsRsp DMCmdPilotSetsRsp; +struct DMCmdExtLogMask { + guint8 code; + /* Bit number of highest '1' in 'mask'; set to 0 to get current mask. */ + guint16 len; + /* Bitfield of log messages to receive */ + guint8 mask[512]; +} __attribute__ ((packed)); +typedef struct DMCmdExtLogMask DMCmdExtLogMask; + +struct DMCmdEventReport { + guint8 code; + guint8 on; +} __attribute__ ((packed)); +typedef struct DMCmdEventReport DMCmdEventReport; + +struct DMCmdEventReportRsp { + guint8 code; + guint16 len; + guint16 event_id; + guint8 data[0]; +} __attribute__ ((packed)); +typedef struct DMCmdEventReportRsp DMCmdEventReportRsp; + /* DIAG_SUBSYS_NW_CONTROL_* subsys command */ struct DMCmdSubsysNwSnapshotReq { DMCmdSubsysHeader hdr; diff --git a/libqcdm/src/error.c b/libqcdm/src/error.c index e3b97a0..994608e 100644 --- a/libqcdm/src/error.c +++ b/libqcdm/src/error.c @@ -75,6 +75,7 @@ qcdm_command_error_get_type (void) ENUM_ENTRY (QCDM_COMMAND_NOT_ACCEPTED, "QcdmCommandNotAccepted"), ENUM_ENTRY (QCDM_COMMAND_BAD_MODE, "QcdmCommandBadMode"), ENUM_ENTRY (QCDM_COMMAND_NVCMD_FAILED, "QcdmCommandNvCmdFailed"), + ENUM_ENTRY (QCDM_COMMAND_SPC_LOCKED, "QcdmCommandSpcLocked"), { 0, 0, 0 } }; diff --git a/libqcdm/src/error.h b/libqcdm/src/error.h index 7a02ae2..f0b0534 100644 --- a/libqcdm/src/error.h +++ b/libqcdm/src/error.h @@ -41,6 +41,7 @@ enum { QCDM_COMMAND_NOT_ACCEPTED = 5, QCDM_COMMAND_BAD_MODE = 6, QCDM_COMMAND_NVCMD_FAILED = 7, + QCDM_COMMAND_SPC_LOCKED = 8, }; #define QCDM_COMMAND_ERROR (qcdm_command_error_quark ()) diff --git a/libqcdm/src/libqcdm.ver b/libqcdm/src/libqcdm.ver deleted file mode 100644 index b1567e2..0000000 --- a/libqcdm/src/libqcdm.ver +++ /dev/null @@ -1,6 +0,0 @@ -{ -global: - nm_vpn_connection_new; -local: - *; -}; diff --git a/libqcdm/src/nv-items.h b/libqcdm/src/nv-items.h index a0ca10a..8240866 100644 --- a/libqcdm/src/nv-items.h +++ b/libqcdm/src/nv-items.h @@ -19,17 +19,21 @@ #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 */ + 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 */ + DIAG_NV_HDR_REV_PREF = 4964, /* HDR mode preference(?): rev0, revA, eHRPD */ }; /* 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_AUTO = 0x04, + DIAG_NV_MODE_PREF_1X_ONLY = 0x09, + DIAG_NV_MODE_PREF_HDR_ONLY = 0x0A, + DIAG_NV_MODE_PREF_1X_HDR_ONLY = 0x0D, + DIAG_NV_MODE_PREF_LTE_ONLY = 0x1E, + DIAG_NV_MODE_PREF_1X_HDR_LTE_ONLY = 0x24, }; /* DIAG_NV_MODE_PREF */ @@ -60,5 +64,18 @@ struct DMNVItemRoamPref { } __attribute__ ((packed)); typedef struct DMNVItemRoamPref DMNVItemRoamPref; +/* HDR Revision preference values (?) */ +enum { + DIAG_NV_HDR_REV_PREF_0 = 0x00, + DIAG_NV_HDR_REV_PREF_A = 0x01, + DIAG_NV_HDR_REV_PREF_EHRPD = 0x04, +}; + +/* DIAG_NV_HDR_REV_PREF */ +struct DMNVItemHdrRevPref { + guint8 rev_pref; +} __attribute__ ((packed)); +typedef struct DMNVItemHdrRevPref DMNVItemHdrRevPref; + #endif /* LIBQCDM_NV_ITEMS_H */ diff --git a/libqcdm/tests/test-qcdm-com.c b/libqcdm/tests/test-qcdm-com.c index f41d249..b95c7d9 100644 --- a/libqcdm/tests/test-qcdm-com.c +++ b/libqcdm/tests/test-qcdm-com.c @@ -38,19 +38,19 @@ prev_to_string (guint8 prev) { switch (prev) { case QCDM_CDMA_PREV_IS_95: - return "IS-95"; + return "1 (IS-95)"; case QCDM_CDMA_PREV_IS_95A: - return "IS-95A"; + return "2 (IS-95A)"; case QCDM_CDMA_PREV_IS_95A_TSB74: - return "IS-95A TSB-74"; + return "3 (IS-95A TSB-74)"; case QCDM_CDMA_PREV_IS_95B_PHASE1: - return "IS-95B Phase I"; + return "4 (IS-95B Phase I)"; case QCDM_CDMA_PREV_IS_95B_PHASE2: - return "IS-95B Phase II"; + return "5 (IS-95B Phase II)"; case QCDM_CDMA_PREV_IS2000_REL0: - return "IS-2000 Release 0"; + return "6 (IS-2000 Release 0)"; case QCDM_CDMA_PREV_IS2000_RELA: - return "IS-2000 Release A"; + return "7 (IS-2000 Release A)"; default: break; } @@ -107,6 +107,54 @@ hdr_rev_to_string (guint8 hdr_rev) return "unknown"; } +static const char * +status_snapshot_state_to_string (guint8 state) +{ + switch (state) { + case QCDM_CMD_STATUS_SNAPSHOT_STATE_NO_SERVICE: + return "no service"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_INITIALIZATION: + return "initialization"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_IDLE: + return "idle"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_VOICE_CHANNEL_INIT: + return "voice channel init"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ORDER: + return "waiting for order"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_WAITING_FOR_ANSWER: + return "waiting for answer"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_CONVERSATION: + return "conversation"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_RELEASE: + return "release"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_SYSTEM_ACCESS: + return "system access"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_CDMA: + return "offline CDMA"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_HDR: + return "offline HDR"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_OFFLINE_ANALOG: + return "offline analog"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_RESET: + return "reset"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_DOWN: + return "power down"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_SAVE: + return "power save"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_POWER_UP: + return "power up"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_LOW_POWER_MODE: + return "low power mode"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_SEARCHER_DSMM: + return "searcher DSMM"; + case QCDM_CMD_STATUS_SNAPSHOT_STATE_HDR: + return "HDR"; + default: + break; + } + return "unknown"; +} + /************************************************************/ typedef struct { @@ -442,6 +490,9 @@ test_com_read_roam_pref (void *f, void *data) /* Parse the response into a result structure */ result = qcdm_cmd_nv_get_roam_pref_result (buf, reply_len, &error); + if (error && (error->code == QCDM_COMMAND_NVCMD_FAILED)) + return; + g_assert (result); g_print ("\n"); @@ -493,7 +544,9 @@ test_com_read_mode_pref (void *f, void *data) /* 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); + g_assert (error); + g_assert (error->domain == QCDM_COMMAND_ERROR); + g_assert (error->code == QCDM_COMMAND_NVCMD_FAILED || error->code == QCDM_COMMAND_BAD_PARAMETER); return; } @@ -521,6 +574,62 @@ test_com_read_mode_pref (void *f, void *data) qcdm_result_unref (result); } +void +test_com_read_hdr_rev_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_hdr_rev_pref_new (buf, sizeof (buf), 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_hdr_rev_pref_result (buf, reply_len, &error); + if (!result) { + g_assert (error); + g_assert (error->domain == QCDM_COMMAND_ERROR); + g_assert (error->code == QCDM_COMMAND_NVCMD_FAILED || error->code == QCDM_COMMAND_BAD_PARAMETER); + return; + } + + g_print ("\n"); + + success = qcdm_result_get_uint8 (result, QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF, &pref); + g_assert (success); + + switch (pref) { + case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_0: + msg = "rev0"; + break; + case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A: + msg = "revA"; + break; + case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD: + msg = "eHRPD"; + break; + default: + msg = "unknown"; + break; + } + g_message ("%s: HDR rev preference: 0x%02X (%s)", __func__, pref, msg); + + qcdm_result_unref (result); +} + void test_com_status (void *f, void *data) { @@ -683,6 +792,57 @@ test_com_sw_version (void *f, void *data) */ } +void +test_com_status_snapshot (void *f, void *data) +{ + TestComData *d = data; + gboolean success; + GError *error = NULL; + char buf[100]; + gint len; + QCDMResult *result; + gsize reply_len; + guint8 n8; + + len = qcdm_cmd_status_snapshot_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_status_snapshot_result (buf, reply_len, &error); + g_assert (result); + + g_print ("\n"); + + n8 = 0; + qcdm_result_get_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BAND_CLASS, &n8); + g_message ("%s: Band Class: %s", __func__, band_class_to_string (n8)); + + n8 = 0; + qcdm_result_get_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_BASE_STATION_PREV, &n8); + g_message ("%s: Base station P_REV: %s", __func__, prev_to_string (n8)); + + n8 = 0; + qcdm_result_get_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_MOBILE_PREV, &n8); + g_message ("%s: Mobile P_REV: %s", __func__, prev_to_string (n8)); + + n8 = 0; + qcdm_result_get_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_PREV_IN_USE, &n8); + g_message ("%s: P_REV in-use: %s", __func__, prev_to_string (n8)); + + n8 = 0; + qcdm_result_get_uint8 (result, QCDM_CMD_STATUS_SNAPSHOT_ITEM_STATE, &n8); + g_message ("%s: State: %d (%s)", __func__, n8, status_snapshot_state_to_string (n8)); + + qcdm_result_unref (result); +} + void test_com_pilot_sets (void *f, void *data) { @@ -802,6 +962,9 @@ test_com_cm_subsys_state_info (void *f, void *data) case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA: detail = "WCDMA"; break; + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_LTE: + detail = "LTE"; + break; default: detail = "unknown"; break; @@ -1090,6 +1253,108 @@ test_com_hdr_subsys_state_info (void *f, void *data) qcdm_result_unref (result); } +void +test_com_ext_logmask (void *f, void *data) +{ + TestComData *d = data; + gboolean success; + GError *error = NULL; + char buf[520]; + gint len; + QCDMResult *result; + gsize reply_len; + GSList *items = NULL; + guint32 maxlog = 0; + + /* First get # of items the device supports */ + len = qcdm_cmd_ext_logmask_new (buf, sizeof (buf), NULL, 0, NULL); + + /* 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_ext_logmask_result (buf, reply_len, &error); + g_assert (result); + + qcdm_result_get_uint32 (result, QCDM_CMD_EXT_LOGMASK_ITEM_MAX_ITEMS, &maxlog); + g_message ("%s: Max # Log Items: %u (0x%X)", __func__, maxlog, maxlog); + + qcdm_result_unref (result); + + /* Now enable some log items */ + items = g_slist_append (items, GUINT_TO_POINTER (0x002C)); + items = g_slist_append (items, GUINT_TO_POINTER (0x002E)); + len = qcdm_cmd_ext_logmask_new (buf, sizeof (buf), items, (guint16) maxlog, NULL); + g_slist_free (items); + + /* 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_ext_logmask_result (buf, reply_len, &error); + g_assert (result); + + qcdm_result_unref (result); + + /* Wait for a log packet */ + reply_len = wait_reply (d, buf, sizeof (buf)); +} + +void +test_com_event_report (void *f, void *data) +{ + TestComData *d = data; + gboolean success; + GError *error = NULL; + char buf[520]; + gint len; + QCDMResult *result; + gsize reply_len; + + /* Turn event reporting on */ + len = qcdm_cmd_event_report_new (buf, sizeof (buf), TRUE, NULL); + + /* 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_event_report_result (buf, reply_len, &error); + g_assert (result); + + qcdm_result_unref (result); + + /* Wait for an event */ + reply_len = wait_reply (d, buf, sizeof (buf)); + + /* Turn event reporting off */ + len = qcdm_cmd_event_report_new (buf, sizeof (buf), FALSE, NULL); + + /* Send the command */ + success = send_command (d, buf, len); + g_assert (success); + + /* Get a response */ + reply_len = wait_reply (d, buf, sizeof (buf)); +} + void test_com_zte_subsys_status (void *f, void *data) { diff --git a/libqcdm/tests/test-qcdm-com.h b/libqcdm/tests/test-qcdm-com.h index 3cf7c4f..76075e5 100644 --- a/libqcdm/tests/test-qcdm-com.h +++ b/libqcdm/tests/test-qcdm-com.h @@ -33,16 +33,24 @@ void test_com_read_roam_pref (void *f, void *data); void test_com_read_mode_pref (void *f, void *data); +void test_com_read_hdr_rev_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_status_snapshot (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_ext_logmask (void *f, void *data); + +void test_com_event_report (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); diff --git a/libqcdm/tests/test-qcdm-utils.c b/libqcdm/tests/test-qcdm-utils.c index 27ec8d6..04807c1 100644 --- a/libqcdm/tests/test-qcdm-utils.c +++ b/libqcdm/tests/test-qcdm-utils.c @@ -84,3 +84,23 @@ test_utils_encapsulate_buffer (void *f, void *data) g_assert (memcmp (outbuf, encap_outbuf, encap_len) == 0); } +static const char cns_inbuf[] = { + 0x00, 0x0a, 0x6b, 0x74, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e +}; + +void +test_utils_decapsulate_sierra_cns (void *f, void *data) +{ + gboolean success; + char outbuf[512]; + gsize decap_len = 0; + gsize used = 0; + gboolean more = FALSE; + + success = dm_decapsulate_buffer (cns_inbuf, sizeof (cns_inbuf), + outbuf, sizeof (outbuf), + &decap_len, &used, &more); + g_assert (success == FALSE); +} + diff --git a/libqcdm/tests/test-qcdm-utils.h b/libqcdm/tests/test-qcdm-utils.h index 8038666..65a9944 100644 --- a/libqcdm/tests/test-qcdm-utils.h +++ b/libqcdm/tests/test-qcdm-utils.h @@ -22,5 +22,7 @@ void test_utils_decapsulate_buffer (void *f, void *data); void test_utils_encapsulate_buffer (void *f, void *data); +void test_utils_decapsulate_sierra_cns (void *f, void *data); + #endif /* TEST_QCDM_UTILS_H */ diff --git a/libqcdm/tests/test-qcdm.c b/libqcdm/tests/test-qcdm.c index a58780e..946fb67 100644 --- a/libqcdm/tests/test-qcdm.c +++ b/libqcdm/tests/test-qcdm.c @@ -28,7 +28,11 @@ typedef struct { gpointer com_data; } TestData; +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else typedef void (*TCFunc)(void); +#endif #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) @@ -85,6 +89,7 @@ int main (int argc, char **argv) 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_utils_decapsulate_sierra_cns, 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)); @@ -97,11 +102,15 @@ int main (int argc, char **argv) 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_read_hdr_rev_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_status_snapshot, 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_ext_logmask, data->com_data)); + g_test_suite_add (suite, TESTCASE (test_com_event_report, 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)); } diff --git a/marshallers/Makefile.am b/marshallers/Makefile.am index 11ce370..0d8d8f7 100644 --- a/marshallers/Makefile.am +++ b/marshallers/Makefile.am @@ -13,9 +13,9 @@ libmarshallers_la_CPPFLAGS = $(MM_CFLAGS) libmarshallers_la_LIBADD = $(MM_LIBS) mm-marshal.h: mm-marshal.list - $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --header > $@ + $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --header > $@ mm-marshal.c: mm-marshal.list - $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --body > $@ + $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --body > $@ mm-marshal-main.c: mm-marshal.c mm-marshal.h diff --git a/marshallers/mm-marshal.list b/marshallers/mm-marshal.list index c209bbb..81fcadb 100644 --- a/marshallers/mm-marshal.list +++ b/marshallers/mm-marshal.list @@ -6,4 +6,5 @@ VOID:UINT,UINT VOID:UINT,UINT,UINT VOID:STRING,BOXED VOID:POINTER,UINT +VOID:STRING,BOXED,BOXED diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules index 8804036..51cc06f 100644 --- a/plugins/77-mm-ericsson-mbm.rules +++ b/plugins/77-mm-ericsson-mbm.rules @@ -17,18 +17,30 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190a", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{ID_MM_ERICSSON_MBM}="1" +# Ericsson F3307 R2 +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1914", 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" +# Ericsson F5521gw +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", 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" +# Sony-Ericsson MD400G +ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d103", ENV{ID_MM_ERICSSON_MBM}="1" + +# Dell 5550 +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818d", ENV{ID_MM_ERICSSON_MBM}="1" + # Dell 5530 HSDPA ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1" @@ -40,6 +52,18 @@ ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{ID_MM_ERICSSON_MBM}="1" +# HP hs2330 Mobile Broadband Module +ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="271d", ENV{ID_MM_ERICSSON_MBM}="1" + +# HP hs2320 Mobile Broadband Module +ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="261d", ENV{ID_MM_ERICSSON_MBM}="1" + +# HP lc2000 Mobile Broadband Module +ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="301d", ENV{ID_MM_ERICSSON_MBM}="1" + +# HP lc2010 Mobile Broadband Module +ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="2f1d", ENV{ID_MM_ERICSSON_MBM}="1" + # Toshiba ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1" @@ -47,5 +71,14 @@ ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{ID_MM_ERICSSON_MBM}="1" ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{ID_MM_ERICSSON_MBM}="1" +# Toshiba F3307 +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1315", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1316", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1317", ENV{ID_MM_ERICSSON_MBM}="1" + +# Toshiba F5521gw +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1313", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1314", ENV{ID_MM_ERICSSON_MBM}="1" + LABEL="mm_mbm_end" diff --git a/plugins/77-mm-x22x-port-types.rules b/plugins/77-mm-x22x-port-types.rules new file mode 100644 index 0000000..0f870a4 --- /dev/null +++ b/plugins/77-mm-x22x-port-types.rules @@ -0,0 +1,30 @@ +# do not edit this file, it will be overwritten on update + +# Alcatel One Touch X220D +# +# These values were scraped from the X220D's Windows .inf files. jrdmdm.inf +# lists the actual command and data (ie PPP) ports, while jrdser.inf lists the +# aux ports that may be either AT-capable or not but cannot be used for PPP. + + +ACTION!="add|change", GOTO="mm_x22x_port_types_end" +SUBSYSTEM!="tty", GOTO="mm_x22x_port_types_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bbb", GOTO="mm_x22x_vendorcheck" +GOTO="mm_x22x_port_types_end" + +LABEL="mm_x22x_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_X22X_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0017", ENV{ID_MM_X22X_TAGGED}="1" + +GOTO="mm_x22x_port_types_end" + + +LABEL="mm_x22x_port_types_end" + diff --git a/plugins/77-mm-zte-port-types.rules b/plugins/77-mm-zte-port-types.rules index 9d64aa9..072154c 100644 --- a/plugins/77-mm-zte-port-types.rules +++ b/plugins/77-mm-zte-port-types.rules @@ -78,6 +78,9 @@ ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}= ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" @@ -93,9 +96,15 @@ ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}= ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" @@ -108,8 +117,56 @@ ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_AUX}= ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + LABEL="mm_zte_port_types_end" diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 8192653..dd58b94 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,3 +1,24 @@ + +########################## +# Icera-specific support # +########################## + +noinst_LTLIBRARIES = libicera-utils.la + +libicera_utils_la_SOURCES = \ + mm-modem-icera.c \ + mm-modem-icera.h + +libicera_utils_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libicera_utils_la_LIBADD = \ + $(GUDEV_LDFLAGS) + +######################################## + pkglib_LTLIBRARIES = \ libmm-plugin-generic.la \ libmm-plugin-moto-c.la \ @@ -12,7 +33,9 @@ pkglib_LTLIBRARIES = \ libmm-plugin-mbm.la \ libmm-plugin-longcheer.la \ libmm-plugin-anydata.la \ - libmm-plugin-simtech.la + libmm-plugin-simtech.la \ + libmm-plugin-x22x.la \ + libmm-plugin-linktop.la # Generic @@ -95,8 +118,8 @@ libmm_plugin_hso_la_SOURCES = \ mm-modem-hso.c \ mm-modem-hso.h -mm-modem-gsm-hso-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-hso.xml - dbus-binding-tool --prefix=mm_modem_gsm_hso --mode=glib-server --output=$@ $< +mm-modem-gsm-hso-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Gsm.Hso.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_hso --mode=glib-server --output=$@ $< libmm_plugin_hso_la_CPPFLAGS = \ $(MM_CFLAGS) \ @@ -177,7 +200,8 @@ libmm_plugin_novatel_la_SOURCES = \ libmm_plugin_novatel_la_CPPFLAGS = \ $(MM_CFLAGS) \ $(GUDEV_CFLAGS) \ - -I$(top_srcdir)/src + -I$(top_srcdir)/src \ + -I$(top_srcdir) libmm_plugin_novatel_la_LDFLAGS = \ $(GUDEV_LDFLAGS) \ @@ -217,6 +241,7 @@ libmm_plugin_zte_la_CPPFLAGS = \ libmm_plugin_zte_la_LDFLAGS = \ $(GUDEV_LDFLAGS) \ + $(builddir)/libicera-utils.la \ -module \ -avoid-version @@ -274,13 +299,49 @@ libmm_plugin_simtech_la_LDFLAGS = \ -module \ -avoid-version +# Alcatel/TCT/JRD x220D and possibly others + +libmm_plugin_x22x_la_SOURCES = \ + mm-plugin-x22x.c \ + mm-plugin-x22x.h \ + mm-modem-x22x-gsm.c \ + mm-modem-x22x-gsm.h + +libmm_plugin_x22x_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_x22x_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Linktop + +libmm_plugin_linktop_la_SOURCES = \ + mm-plugin-linktop.c \ + mm-plugin-linktop.h \ + mm-modem-linktop.c \ + mm-modem-linktop.h + +libmm_plugin_linktop_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_linktop_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-simtech-port-types.rules + 77-mm-simtech-port-types.rules \ + 77-mm-x22x-port-types.rules BUILT_SOURCES = \ mm-modem-gsm-hso-glue.h diff --git a/plugins/mm-modem-anydata-cdma.c b/plugins/mm-modem-anydata-cdma.c index c7cca46..0e7e65c 100644 --- a/plugins/mm-modem-anydata-cdma.c +++ b/plugins/mm-modem-anydata-cdma.c @@ -28,6 +28,7 @@ #include "mm-callback-info.h" #include "mm-serial-port.h" #include "mm-serial-parsers.h" +#include "mm-log.h" static void modem_init (MMModem *modem_class); @@ -39,7 +40,9 @@ mm_modem_anydata_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA) + gboolean evdo_revA, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -52,6 +55,8 @@ mm_modem_anydata_cdma_new (const char *device, MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, FALSE, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -136,7 +141,7 @@ evdo_state_done (MMAtSerialPort *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): *HSTATE parse regex creation failed.", __func__); + mm_warn ("ANYDATA: *HSTATE parse regex creation failed."); goto done; } @@ -163,7 +168,7 @@ evdo_state_done (MMAtSerialPort *port, reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; default: - g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + mm_warn ("ANYDATA: unknown *STATE (%d); assuming no service.", val); /* fall through */ case 0: /* NO SERVICE */ case 1: /* ACQUISITION */ @@ -202,7 +207,7 @@ state_done (MMAtSerialPort *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) { - g_warning ("AnyDATA(%s): *STATE parse regex creation failed.", __func__); + mm_warn ("ANYDATA: *STATE parse regex creation failed."); mm_callback_info_schedule (info); return; } @@ -231,7 +236,7 @@ state_done (MMAtSerialPort *port, reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; break; default: - g_warning ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + mm_warn ("ANYDATA: unknown *STATE (%d); assuming no service.", val); /* fall through */ case 0: /* NO SERVICE */ break; @@ -268,6 +273,24 @@ query_registration_state (MMGenericCdma *cdma, /*****************************************************************************/ +static void +reset (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + /* Ensure we have a usable port to use for the command */ + port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (modem), &info->error); + if (port) + mm_at_serial_port_queue_command (port, "*RESET", 3, NULL, NULL); + + mm_callback_info_schedule (info); +} + static gboolean grab_port (MMModem *modem, const char *subsys, @@ -329,6 +352,7 @@ static void modem_init (MMModem *modem_class) { modem_class->grab_port = grab_port; + modem_class->reset = reset; } static void diff --git a/plugins/mm-modem-anydata-cdma.h b/plugins/mm-modem-anydata-cdma.h index d2695d5..5a9fc4b 100644 --- a/plugins/mm-modem-anydata-cdma.h +++ b/plugins/mm-modem-anydata-cdma.h @@ -40,6 +40,8 @@ MMModem *mm_modem_anydata_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA); + gboolean evdo_revA, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_ANYDATA_CDMA_H */ diff --git a/plugins/mm-modem-gobi-gsm.c b/plugins/mm-modem-gobi-gsm.c index 3b9e9ec..7e2fa4c 100644 --- a/plugins/mm-modem-gobi-gsm.c +++ b/plugins/mm-modem-gobi-gsm.c @@ -36,7 +36,9 @@ G_DEFINE_TYPE_EXTENDED (MMModemGobiGsm, mm_modem_gobi_gsm, MM_TYPE_GENERIC_GSM, MMModem * mm_modem_gobi_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -46,6 +48,8 @@ mm_modem_gobi_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-gobi-gsm.h b/plugins/mm-modem-gobi-gsm.h index 4bd262f..b1af164 100644 --- a/plugins/mm-modem-gobi-gsm.h +++ b/plugins/mm-modem-gobi-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_gobi_gsm_get_type (void); MMModem *mm_modem_gobi_gsm_new (const char *device, const char *driver, - const char *plugin_name); + const char *plugin_name, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_GOBI_GSM_H */ diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c index 1fd4633..ff0264a 100644 --- a/plugins/mm-modem-hso.c +++ b/plugins/mm-modem-hso.c @@ -74,7 +74,9 @@ typedef struct { MMModem * mm_modem_hso_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -85,6 +87,8 @@ mm_modem_hso_new (const char *device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_STATIC, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -427,7 +431,7 @@ unsolicited_disable_done (MMModem *modem, } /* Otherwise, kill any existing connection */ - if (mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)) >= 0) + if (hso_get_cid (MM_MODEM_HSO (modem)) >= 0) hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info); else disable_done (modem, NULL, info); diff --git a/plugins/mm-modem-hso.h b/plugins/mm-modem-hso.h index e2d7623..7e79886 100644 --- a/plugins/mm-modem-hso.h +++ b/plugins/mm-modem-hso.h @@ -38,7 +38,9 @@ GType mm_modem_hso_get_type (void); MMModem *mm_modem_hso_new (const char *device, const char *driver, - const char *plugin); + const char *plugin, + guint32 vendor, + guint32 product); void mm_hso_modem_authenticate (MMModemHso *self, const char *username, diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c index 523578f..3aec470 100644 --- a/plugins/mm-modem-huawei-cdma.c +++ b/plugins/mm-modem-huawei-cdma.c @@ -27,6 +27,7 @@ #include "mm-callback-info.h" #include "mm-serial-port.h" #include "mm-serial-parsers.h" +#include "mm-log.h" static void modem_init (MMModem *modem_class); @@ -39,7 +40,9 @@ mm_modem_huawei_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA) + gboolean evdo_revA, + guint32 vendor, + guint32 product) { gboolean try_css = TRUE; @@ -61,6 +64,8 @@ mm_modem_huawei_cdma_new (const char *device, MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, try_css, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -75,7 +80,7 @@ parse_quality (const char *str, const char *detail) quality = strtol (str, NULL, 10); if (errno == 0) { quality = CLAMP (quality, 0, 100); - g_debug ("%s: %ld", detail, quality); + mm_dbg ("%s: %ld", detail, quality); return (gint) quality; } return -1; @@ -173,7 +178,7 @@ sysinfo_done (MMAtSerialPort *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) { - g_warning ("Huawei(%s): ^SYSINFO parse regex creation failed.", __func__); + mm_warn ("Huawei: ^SYSINFO parse regex creation failed."); goto done; } @@ -214,7 +219,7 @@ sysinfo_done (MMAtSerialPort *port, mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); } } else - g_warning ("Huawei(%s): failed to parse ^SYSINFO response.", __func__); + mm_warn ("Huawei: failed to parse ^SYSINFO response."); g_match_info_free (match_info); g_regex_unref (r); diff --git a/plugins/mm-modem-huawei-cdma.h b/plugins/mm-modem-huawei-cdma.h index 738f2d9..88b425c 100644 --- a/plugins/mm-modem-huawei-cdma.h +++ b/plugins/mm-modem-huawei-cdma.h @@ -40,6 +40,8 @@ MMModem *mm_modem_huawei_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA); + gboolean evdo_revA, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_HUAWEI_CDMA_H */ diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c index 5123e7f..3fc9ae2 100644 --- a/plugins/mm-modem-huawei-gsm.c +++ b/plugins/mm-modem-huawei-gsm.c @@ -29,6 +29,7 @@ #include "mm-callback-info.h" #include "mm-at-serial-port.h" #include "mm-serial-parsers.h" +#include "mm-log.h" static void modem_init (MMModem *modem_class); static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); @@ -50,7 +51,9 @@ typedef struct { MMModem * mm_modem_huawei_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -60,6 +63,8 @@ mm_modem_huawei_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -362,6 +367,35 @@ get_band (MMModemGsmNetwork *modem, mm_at_serial_port_queue_command (port, "AT^SYSCFG?", 3, get_band_done, info); } +static MMModemGsmAccessTech +huawei_sysinfo_to_act (int huawei) +{ + switch (huawei) { + case 1: + return MM_MODEM_GSM_ACCESS_TECH_GSM; + case 2: + return MM_MODEM_GSM_ACCESS_TECH_GPRS; + case 3: + return MM_MODEM_GSM_ACCESS_TECH_EDGE; + case 4: + return MM_MODEM_GSM_ACCESS_TECH_UMTS; + case 5: + return MM_MODEM_GSM_ACCESS_TECH_HSDPA; + case 6: + return MM_MODEM_GSM_ACCESS_TECH_HSUPA; + case 7: + return MM_MODEM_GSM_ACCESS_TECH_HSPA; + case 9: + return MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS; + case 8: + /* TD-SCDMA */ + default: + break; + } + + return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; +} + static void get_act_request_done (MMAtSerialPort *port, GString *response, @@ -404,22 +438,8 @@ get_act_request_done (MMAtSerialPort *port, 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; - } + if (str && strlen (str)) + act = huawei_sysinfo_to_act (atoi (str)); g_free (str); } @@ -514,7 +534,7 @@ send_huawei_cpin_done (MMAtSerialPort *port, else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) num = 5; else { - g_debug ("%s: unhandled pin type '%s'", __func__, pin_type); + mm_dbg ("unhandled pin type '%s'", pin_type); info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type"); } @@ -556,7 +576,7 @@ get_unlock_retries (MMModemGsmCard *modem, char *command; MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - g_debug ("%s: pin type '%s'", __func__, pin_type); + mm_dbg ("pin type '%s'", pin_type); /* Ensure we have a usable port to use for the command */ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); @@ -622,40 +642,30 @@ handle_mode_change (MMAtSerialPort *port, MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; char *str; int a; - int b; str = g_match_info_fetch (match_info, 1); a = atoi (str); g_free (str); str = g_match_info_fetch (match_info, 2); - b = atoi (str); + act = huawei_sysinfo_to_act (atoi (str)); g_free (str); 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; + if (act > MM_MODEM_GSM_ACCESS_TECH_EDGE) + act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; } 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; + if (act < MM_MODEM_GSM_ACCESS_TECH_UMTS) + act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; } else if (a == 0) act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; else { - g_warning ("Couldn't parse mode change value: '%s'", str); + mm_warn ("Couldn't parse mode change value: '%s'", str); return; } - g_debug ("Access Technology: %d", act); + mm_dbg ("Access Technology: %d", act); + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act); } @@ -669,8 +679,8 @@ handle_status_change (MMAtSerialPort *port, str = g_match_info_fetch (match_info, 1); if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7)) { - g_debug ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n", - n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024); + mm_dbg ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n", + n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024); } g_free (str); } diff --git a/plugins/mm-modem-huawei-gsm.h b/plugins/mm-modem-huawei-gsm.h index 9c2ec34..05f1f29 100644 --- a/plugins/mm-modem-huawei-gsm.h +++ b/plugins/mm-modem-huawei-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_huawei_gsm_get_type (void); MMModem *mm_modem_huawei_gsm_new (const char *device, const char *driver, - const char *plugin); + const char *plugin, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_HUAWEI_GSM_H */ diff --git a/plugins/mm-modem-icera.c b/plugins/mm-modem-icera.c new file mode 100644 index 0000000..3f71c5b --- /dev/null +++ b/plugins/mm-modem-icera.c @@ -0,0 +1,794 @@ +/* -*- 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 Icera-based modems + ******************************************/ + +#include +#include +#include +#include +#include +#include + +#include "mm-modem-icera.h" + +#include "mm-modem.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-at-serial-port.h" +#include "mm-generic-gsm.h" +#include "mm-modem-helpers.h" +#include "mm-log.h" + +struct _MMModemIceraPrivate { + /* Pending connection attempt */ + MMCallbackInfo *connect_pending_data; + guint connect_pending_id; + + char *username; + char *password; + + MMModemGsmAccessTech last_act; +}; + +#define MM_MODEM_ICERA_GET_PRIVATE(m) (MM_MODEM_ICERA_GET_INTERFACE(m)->priv) + +static void +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, "%IPSYS: ")) { + 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); +} + +void +mm_modem_icera_get_allowed_mode (MMModemIcera *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + mm_at_serial_port_queue_command (port, "%IPSYS?", 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); +} + +void +mm_modem_icera_set_allowed_mode (MMModemIcera *self, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + int i; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &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 ("%%IPSYS=%d", i); + mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info); + g_free (command); +} + +static MMModemGsmAccessTech +nwstate_to_act (const char *str) +{ + /* small 'g' means CS, big 'G' means PS */ + if (!strcmp (str, "2G-GPRS")) + return MM_MODEM_GSM_ACCESS_TECH_GPRS; + else if (!strcmp (str, "2G-EDGE")) + return MM_MODEM_GSM_ACCESS_TECH_EDGE; + else if (!strcmp (str, "3G")) + return MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (!strcmp (str, "3G-HSDPA")) + return MM_MODEM_GSM_ACCESS_TECH_HSDPA; + else if (!strcmp (str, "3G-HSUPA")) + return MM_MODEM_GSM_ACCESS_TECH_HSUPA; + else if (!strcmp (str, "3G-HSDPA-HSUPA")) + return MM_MODEM_GSM_ACCESS_TECH_HSPA; + + return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; +} + +static void +nwstate_changed (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMModemIcera *self = MM_MODEM_ICERA (user_data); + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + char *str; + int rssi = -1; + + str = g_match_info_fetch (info, 1); + if (str) { + rssi = atoi (str); + rssi = CLAMP (rssi, -1, 5); + g_free (str); + } + + str = g_match_info_fetch (info, 3); + if (str) { + act = nwstate_to_act (str); + g_free (str); + } + + MM_MODEM_ICERA_GET_PRIVATE (self)->last_act = act; + mm_generic_gsm_update_access_technology (MM_GENERIC_GSM (self), act); +} + +static void +pacsp_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + return; +} + +static void +get_nwstate_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->error) { + MMModemIcera *self = MM_MODEM_ICERA (info->modem); + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + + /* The unsolicited message handler will already have run and + * removed the NWSTATE response, so we have to work around that. + */ + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->last_act), NULL); + priv->last_act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + } + + mm_callback_info_schedule (info); +} + +void +mm_modem_icera_get_access_technology (MMModemIcera *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "%NWSTATE=1", 3, get_nwstate_done, info); +} + +/****************************************************************/ + +static void +disconnect_ipdpact_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_callback_info_schedule ((MMCallbackInfo *) user_data); +} + +void +mm_modem_icera_do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *primary; + char *command; + + 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); + + command = g_strdup_printf ("%%IPDPACT=%d,0", cid); + mm_at_serial_port_queue_command (primary, command, 3, disconnect_ipdpact_done, info); + g_free (command); +} + +/*****************************************************************************/ + +static void +connect_pending_done (MMModemIcera *self) +{ + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + GError *error = NULL; + + if (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; + } + + if (priv->connect_pending_id) { + g_source_remove (priv->connect_pending_id); + priv->connect_pending_id = 0; + } +} + +static void +icera_disconnect_done (MMModem *modem, + GError *error, + gpointer user_data) +{ + mm_info ("Modem signaled disconnection from the network"); +} + +static void +connection_enabled (MMAtSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemIcera *self = MM_MODEM_ICERA (user_data); + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + MMCallbackInfo *info = priv->connect_pending_data; + char *str; + int status, cid, tmp; + + cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)); + if (cid < 0) + return; + + str = g_match_info_fetch (match_info, 1); + g_return_if_fail (str != NULL); + tmp = atoi (str); + g_free (str); + + /* Make sure the unsolicited message's CID matches the current CID */ + if (tmp != cid) + return; + + str = g_match_info_fetch (match_info, 2); + g_return_if_fail (str != NULL); + status = atoi (str); + g_free (str); + + switch (status) { + case 0: + /* Disconnected */ + if (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_CONNECTED) + mm_modem_disconnect (MM_MODEM (self), icera_disconnect_done, NULL); + break; + case 1: + /* Connected */ + connect_pending_done (self); + break; + case 2: + /* Connecting */ + break; + case 3: + /* Call setup failure? */ + if (info) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Call setup failed"); + } + connect_pending_done (self); + break; + default: + mm_warn ("Unknown Icera connect status %d", status); + break; + } +} + +/****************************************************************/ + +static gint +_get_cid (MMModemIcera *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 +icera_call_control (MMModemIcera *self, + gboolean activate, + MMAtSerialResponseFn callback, + gpointer user_data) +{ + char *command; + MMAtSerialPort *primary; + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + command = g_strdup_printf ("%%IPDPACT=%d,%d", _get_cid (self), activate ? 1 : 0); + mm_at_serial_port_queue_command (primary, command, 3, callback, user_data); + g_free (command); +} + +static void +timeout_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + connect_pending_done (MM_MODEM_ICERA (user_data)); +} + +static gboolean +icera_connect_timed_out (gpointer data) +{ + MMModemIcera *self = MM_MODEM_ICERA (data); + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + MMCallbackInfo *info = priv->connect_pending_data; + + priv->connect_pending_id = 0; + + if (info) { + info->error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_ERROR_RESPONSE_TIMEOUT, + "Connection timed out"); + } + + icera_call_control (self, FALSE, timeout_done, self); + return FALSE; +} + +static void +icera_enabled (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), error, info); + } else { + MMModemIcera *self = MM_MODEM_ICERA (info->modem); + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + + g_warn_if_fail (priv->connect_pending_id == 0); + if (priv->connect_pending_id) + g_source_remove (priv->connect_pending_id); + + priv->connect_pending_data = info; + priv->connect_pending_id = g_timeout_add_seconds (30, icera_connect_timed_out, self); + } +} + +static void +old_context_clear_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + /* Activate the PDP context and start the data session */ + icera_call_control (MM_MODEM_ICERA (info->modem), TRUE, icera_enabled, info); +} + +static void +auth_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), error, info); + else { + /* Ensure the PDP context is deactivated */ + icera_call_control (MM_MODEM_ICERA (info->modem), FALSE, old_context_clear_done, info); + } +} + +void +mm_modem_icera_do_connect (MMModemIcera *self, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMModem *modem = MM_MODEM (self); + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + MMCallbackInfo *info; + MMAtSerialPort *primary; + gint cid; + char *command; + + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + info = mm_callback_info_new (modem, callback, user_data); + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + cid = _get_cid (self); + + + /* Both user and password are required; otherwise firmware returns an error */ + if (!priv->username || !priv->password) + command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", cid); + else { + command = g_strdup_printf ("%%IPDPCFG=%d,0,1,\"%s\",\"%s\"", + 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 +free_dns_array (gpointer data) +{ + g_array_free ((GArray *) data, TRUE); +} + +static void +ip4_config_invoke (MMCallbackInfo *info) +{ + MMModemIp4Fn callback = (MMModemIp4Fn) info->callback; + + callback (info->modem, + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-address")), + (GArray *) mm_callback_info_get_data (info, "ip4-dns"), + info->error, info->user_data); +} + +#define IPDPADDR_TAG "%IPDPADDR: " + +static void +get_ip4_config_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char **items, **iter; + GArray *dns_array; + int i; + guint32 tmp; + gint cid; + + if (error) { + info->error = g_error_copy (error); + goto out; + } else if (!g_str_has_prefix (response->str, IPDPADDR_TAG)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Retrieving failed: invalid response."); + goto out; + } + + cid = _get_cid (MM_MODEM_ICERA (info->modem)); + dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2); + + /* %IPDPADDR: ,,,,[,,] */ + items = g_strsplit (response->str + strlen (IPDPADDR_TAG), ", ", 0); + + for (iter = items, i = 0; *iter; iter++, i++) { + if (i == 0) { /* CID */ + long int num; + + errno = 0; + num = strtol (*iter, NULL, 10); + if (errno != 0 || num < 0 || (gint) num != cid) { + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Unknown CID in IPDPADDR response (" + "got %d, expected %d)", (guint) num, cid); + break; + } + } else if (i == 1) { /* IP address */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + mm_callback_info_set_data (info, "ip4-address", GUINT_TO_POINTER (tmp), NULL); + } else if (i == 3) { /* DNS 1 */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + g_array_append_val (dns_array, tmp); + } else if (i == 4) { /* DNS 2 */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + g_array_append_val (dns_array, tmp); + } + } + + g_strfreev (items); + mm_callback_info_set_data (info, "ip4-dns", dns_array, free_dns_array); + + out: + mm_callback_info_schedule (info); +} + +void +mm_modem_icera_get_ip4_config (MMModemIcera *self, + MMModemIp4Fn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMAtSerialPort *primary; + + info = mm_callback_info_new_full (MM_MODEM (self), + ip4_config_invoke, + G_CALLBACK (callback), + user_data); + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + command = g_strdup_printf ("%%IPDPADDR=%d", _get_cid (self)); + mm_at_serial_port_queue_command (primary, command, 3, get_ip4_config_done, info); + g_free (command); +} + +/****************************************************************/ + +static const char * +get_string_property (GHashTable *properties, const char *name) +{ + GValue *value; + + value = (GValue *) g_hash_table_lookup (properties, name); + if (value && G_VALUE_HOLDS_STRING (value)) + return g_value_get_string (value); + return NULL; +} + +void +mm_modem_icera_simple_connect (MMModemIcera *self, GHashTable *properties) +{ + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + + g_free (priv->username); + priv->username = g_strdup (get_string_property (properties, "username")); + g_free (priv->password); + priv->password = g_strdup (get_string_property (properties, "password")); +} + +/****************************************************************/ + +void +mm_modem_icera_register_unsolicted_handlers (MMModemIcera *self, + MMAtSerialPort *port) +{ + GRegex *regex; + + /* %NWSTATE: ,,,, */ + regex = g_regex_new ("\\r\\n%NWSTATE:\\s*(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, nwstate_changed, self, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, pacsp_received, self, NULL); + g_regex_unref (regex); + + /* %IPDPACT: ,,0 */ + regex = g_regex_new ("\\r\\n%IPDPACT:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (port, regex, connection_enabled, self, NULL); + g_regex_unref (regex); +} + +void +mm_modem_icera_change_unsolicited_messages (MMModemIcera *self, gboolean enabled) +{ + MMAtSerialPort *primary; + + 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, enabled ? "%NWSTATE=1" : "%NWSTATE=0", 3, NULL, NULL); +} + +/****************************************************************/ + +static void +is_icera_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->error) + mm_callback_info_set_result (info, GUINT_TO_POINTER (TRUE), NULL); + mm_callback_info_schedule (info); +} + +void +mm_modem_icera_is_icera (MMModemIcera *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "%IPSYS?", 5, is_icera_done, info); +} + +/****************************************************************/ + +void +mm_modem_icera_prepare (MMModemIcera *self) +{ + self->priv = g_malloc0 (sizeof (MMModemIceraPrivate)); +} + +void +mm_modem_icera_cleanup (MMModemIcera *self) +{ + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + + /* Clear the pending connection if necessary */ + connect_pending_done (self); + + g_free (priv->username); + g_free (priv->password); + + memset (priv, 0, sizeof (MMModemIceraPrivate)); + g_free (priv); +} + +static void +mm_modem_icera_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + initialized = TRUE; + } +} + +GType +mm_modem_icera_get_type (void) +{ + static GType icera_type = 0; + + if (!G_UNLIKELY (icera_type)) { + const GTypeInfo icera_info = { + sizeof (MMModemIcera), /* class_size */ + mm_modem_icera_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + icera_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemIcera", + &icera_info, 0); + + g_type_interface_add_prerequisite (icera_type, MM_TYPE_MODEM); + } + + return icera_type; +} + diff --git a/plugins/mm-modem-icera.h b/plugins/mm-modem-icera.h new file mode 100644 index 0000000..71258d2 --- /dev/null +++ b/plugins/mm-modem-icera.h @@ -0,0 +1,89 @@ +/* -*- 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 Icera-based modems + ******************************************/ + +#ifndef MM_MODEM_ICERA_H +#define MM_MODEM_ICERA_H + +#include + +#include "mm-modem-gsm.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_ICERA (mm_modem_icera_get_type ()) +#define MM_MODEM_ICERA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_ICERA, MMModemIcera)) +#define MM_IS_MODEM_ICERA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_ICERA)) +#define MM_MODEM_ICERA_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_ICERA, MMModemIcera)) + +typedef struct _MMModemIceraPrivate MMModemIceraPrivate; + +typedef struct _MMModemIcera MMModemIcera; + +struct _MMModemIcera { + GTypeInterface g_iface; + + MMModemIceraPrivate *priv; +}; + +GType mm_modem_icera_get_type (void); + +void mm_modem_icera_prepare (MMModemIcera *self); + +void mm_modem_icera_cleanup (MMModemIcera *self); + +void mm_modem_icera_get_allowed_mode (MMModemIcera *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_icera_set_allowed_mode (MMModemIcera *self, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data); + +void mm_modem_icera_register_unsolicted_handlers (MMModemIcera *self, + MMAtSerialPort *port); + +void mm_modem_icera_change_unsolicited_messages (MMModemIcera *self, + gboolean enabled); + +void mm_modem_icera_get_access_technology (MMModemIcera *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_icera_is_icera (MMModemIcera *self, + MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_icera_do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data); + +void mm_modem_icera_simple_connect (MMModemIcera *self, GHashTable *properties); + +void mm_modem_icera_do_connect (MMModemIcera *self, + const char *number, + MMModemFn callback, + gpointer user_data); + +void mm_modem_icera_get_ip4_config (MMModemIcera *self, + MMModemIp4Fn callback, + gpointer user_data); + +#endif /* MM_MODEM_ICERA_H */ + diff --git a/plugins/mm-modem-linktop.c b/plugins/mm-modem-linktop.c new file mode 100644 index 0000000..923c219 --- /dev/null +++ b/plugins/mm-modem-linktop.c @@ -0,0 +1,208 @@ +/* -*- 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. + */ + +#include +#include +#include +#include +#include "mm-modem-linktop.h" +#include "mm-serial-parsers.h" +#include "mm-errors.h" + +#define LINKTOP_NETWORK_MODE_ANY 1 +#define LINKTOP_NETWORK_MODE_2G 5 +#define LINKTOP_NETWORK_MODE_3G 6 + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemLinktop, mm_modem_linktop, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_linktop_new (const char *device, + const char *driver, + const char *plugin, + guint32 vendor, + guint32 product) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_LINKTOP, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, + NULL)); +} + +static 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)) { + 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; +} + +static int +linktop_parse_allowed_mode (MMModemGsmAllowedMode network_mode) +{ + switch (network_mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + return LINKTOP_NETWORK_MODE_2G; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + return LINKTOP_NETWORK_MODE_3G; + default: + return LINKTOP_NETWORK_MODE_ANY; + } +} + +static void +linktop_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; + char *command; + MMAtSerialPort *port; + + 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", linktop_parse_allowed_mode (mode)); + mm_at_serial_port_queue_command (port, command, 3, linktop_set_allowed_mode_done, info); + g_free (command); +} + +static void +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, "CFUN: ")) { + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + int a; + + a = atoi (response->str + 6); + if (a == LINKTOP_NETWORK_MODE_2G) + mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + else if (a == LINKTOP_NETWORK_MODE_3G) + mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + + 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 +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, "+CFUN?", 3, get_allowed_mode_done, info); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_linktop_init (MMModemLinktop *self) +{ +} + +static void +mm_modem_linktop_class_init (MMModemLinktopClass *klass) +{ + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->set_allowed_mode = set_allowed_mode; +} + diff --git a/plugins/mm-modem-linktop.h b/plugins/mm-modem-linktop.h new file mode 100644 index 0000000..4b7153e --- /dev/null +++ b/plugins/mm-modem-linktop.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_LINKTOP_H +#define MM_MODEM_LINKTOP_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_LINKTOP (mm_modem_linktop_get_type ()) +#define MM_MODEM_LINKTOP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_LINKTOP, MMModemLinktop)) +#define MM_MODEM_LINKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_LINKTOP, MMModemLinktopClass)) +#define MM_IS_MODEM_LINKTOP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_LINKTOP)) +#define MM_IS_MODEM_LINKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_LINKTOP)) +#define MM_MODEM_LINKTOP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_LINKTOP, MMModemLinktopClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemLinktop; + +typedef struct { + MMGenericGsmClass parent; +} MMModemLinktopClass; + +GType mm_modem_linktop_get_type (void); + +MMModem *mm_modem_linktop_new (const char *data, + const char *driver, + const char *plugin, + guint32 vendor, + guint32 product); + +#endif /* MM_MODEM_LINKTOP_H */ diff --git a/plugins/mm-modem-longcheer-gsm.c b/plugins/mm-modem-longcheer-gsm.c index 62980f7..33763e2 100644 --- a/plugins/mm-modem-longcheer-gsm.c +++ b/plugins/mm-modem-longcheer-gsm.c @@ -30,7 +30,9 @@ 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) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -40,6 +42,8 @@ mm_modem_longcheer_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-longcheer-gsm.h b/plugins/mm-modem-longcheer-gsm.h index 5383c52..e9abfbe 100644 --- a/plugins/mm-modem-longcheer-gsm.h +++ b/plugins/mm-modem-longcheer-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_longcheer_gsm_get_type (void); MMModem *mm_modem_longcheer_gsm_new (const char *device, const char *driver, - const char *plugin); + const char *plugin, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_LONGCHEER_H */ diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c index 7f6bc9c..9303453 100644 --- a/plugins/mm-modem-mbm.c +++ b/plugins/mm-modem-mbm.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Copyright (C) 2008 - 2010 Ericsson AB - * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. * * Author: Per Hallsmark * Bjorn Runaker @@ -31,6 +31,7 @@ #include "mm-modem-gsm-card.h" #include "mm-errors.h" #include "mm-callback-info.h" +#include "mm-log.h" static void modem_init (MMModem *modem_class); static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); @@ -49,8 +50,6 @@ G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0, #define MBM_E2NAP_CONNECTED 1 #define MBM_E2NAP_CONNECTING 2 -#define MBM_SIGNAL_INDICATOR 2 - #define MBM_NETWORK_MODE_ANY 1 #define MBM_NETWORK_MODE_2G 5 #define MBM_NETWORK_MODE_3G 6 @@ -82,7 +81,9 @@ mbm_modem_authenticate (MMModemMbm *self, MMModem * mm_modem_mbm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -93,6 +94,8 @@ mm_modem_mbm_new (const char *device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -349,7 +352,6 @@ mbm_enable_done (MMAtSerialPort *port, 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); @@ -409,7 +411,7 @@ mbm_emrdy_done (MMAtSerialPort *port, MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) - g_warning ("%s: timed out waiting for EMRDY response.", __func__); + mm_warn ("timed out waiting for EMRDY response."); else priv->have_emrdy = TRUE; @@ -473,7 +475,7 @@ disable (MMModem *modem, g_assert (primary); /* Turn off unsolicited responses */ - mm_at_serial_port_queue_command (primary, "+CMER=0;*ERINFO=0", 5, disable_unsolicited_done, info); + mm_at_serial_port_queue_command (primary, "*ERINFO=0", 5, disable_unsolicited_done, info); } static void @@ -508,6 +510,24 @@ do_disconnect (MMGenericGsm *gsm, MM_GENERIC_GSM_CLASS (mm_modem_mbm_parent_class)->do_disconnect (gsm, cid, callback, user_data); } +static void +reset (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + /* 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_at_serial_port_queue_command (port, "*E2RESET", 3, NULL, NULL); + + mm_callback_info_schedule (info); +} + static void factory_reset_done (MMAtSerialPort *port, GString *response, @@ -579,29 +599,6 @@ mbm_pacsp_received (MMAtSerialPort *port, return; } -static void -mbm_ciev_received (MMAtSerialPort *port, - GMatchInfo *info, - gpointer user_data) -{ - int quality = 0, ind = 0; - 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_generic_gsm_update_signal_quality (MM_GENERIC_GSM (user_data), quality * 20); - } - g_free (str); - } -} - static void mbm_do_connect_done (MMModemMbm *self, gboolean success) { @@ -636,16 +633,16 @@ mbm_e2nap_received (MMAtSerialPort *port, g_free (str); if (MBM_E2NAP_DISCONNECTED == state) { - g_debug ("%s: disconnected", __func__); + mm_dbg ("disconnected"); mbm_do_connect_done (MM_MODEM_MBM (user_data), FALSE); } else if (MBM_E2NAP_CONNECTED == state) { - g_debug ("%s: connected", __func__); + mm_dbg ("connected"); mbm_do_connect_done (MM_MODEM_MBM (user_data), TRUE); } else if (MBM_E2NAP_CONNECTING == state) - g_debug("%s: connecting", __func__); + mm_dbg ("connecting"); else { /* Should not happen */ - g_debug("%s: unhandled E2NAP state %d", __func__, state); + mm_dbg ("unhandled E2NAP state %d", state); mbm_do_connect_done (MM_MODEM_MBM (user_data), FALSE); } } @@ -724,18 +721,15 @@ mbm_auth_done (MMSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMGenericGsm *modem = MM_GENERIC_GSM (info->modem); char *command; - guint32 cid; if (error) { mm_generic_gsm_connect_complete (modem, error, info); return; } - 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); + command = g_strdup_printf ("AT*ENAP=1,%d", mm_generic_gsm_get_cid (modem)); mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), command, 3, enap_done, user_data); g_free (command); } @@ -818,7 +812,7 @@ send_epin_done (MMAtSerialPort *port, 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); + mm_dbg ("unhandled pin type '%s'", pin_type); info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type"); } @@ -845,7 +839,7 @@ mbm_get_unlock_retries (MMModemGsmCard *modem, char *command; MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); - g_debug ("%s: pin type '%s'", __func__, pin_type); + mm_dbg ("pin type '%s'", pin_type); /* Ensure we have a usable port to use for the command */ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); @@ -922,10 +916,6 @@ grab_port (MMModem *modem, 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_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_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, NULL, NULL, NULL); @@ -969,6 +959,7 @@ modem_init (MMModem *modem_class) modem_class->grab_port = grab_port; modem_class->disable = disable; modem_class->connect = do_connect; + modem_class->reset = reset; modem_class->factory_reset = factory_reset; } diff --git a/plugins/mm-modem-mbm.h b/plugins/mm-modem-mbm.h index db0f627..bb1c2ff 100644 --- a/plugins/mm-modem-mbm.h +++ b/plugins/mm-modem-mbm.h @@ -43,6 +43,8 @@ GType mm_modem_mbm_get_type (void); MMModem *mm_modem_mbm_new (const char *device, const char *driver, - const char *plugin_name); + const char *plugin_name, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_MBM_H */ diff --git a/plugins/mm-modem-moto-c-gsm.c b/plugins/mm-modem-moto-c-gsm.c index 5910ff2..cd7b4ed 100644 --- a/plugins/mm-modem-moto-c-gsm.c +++ b/plugins/mm-modem-moto-c-gsm.c @@ -35,7 +35,9 @@ G_DEFINE_TYPE_EXTENDED (MMModemMotoCGsm, mm_modem_moto_c_gsm, MM_TYPE_GENERIC_GS MMModem * mm_modem_moto_c_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -45,6 +47,8 @@ mm_modem_moto_c_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-moto-c-gsm.h b/plugins/mm-modem-moto-c-gsm.h index eb1dad1..a8ddd6d 100644 --- a/plugins/mm-modem-moto-c-gsm.h +++ b/plugins/mm-modem-moto-c-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_moto_c_gsm_get_type (void); MMModem *mm_modem_moto_c_gsm_new (const char *device, const char *driver, - const char *plugin_name); + const char *plugin_name, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_MOTO_C_GSM_H */ diff --git a/plugins/mm-modem-nokia.c b/plugins/mm-modem-nokia.c index eb90287..56b8c91 100644 --- a/plugins/mm-modem-nokia.c +++ b/plugins/mm-modem-nokia.c @@ -30,7 +30,9 @@ G_DEFINE_TYPE_EXTENDED (MMModemNokia, mm_modem_nokia, MM_TYPE_GENERIC_GSM, 0, MMModem * mm_modem_nokia_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -40,6 +42,8 @@ mm_modem_nokia_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -54,6 +58,7 @@ grab_port (MMModem *modem, MMGenericGsm *gsm = MM_GENERIC_GSM (modem); MMPortType ptype = MM_PORT_TYPE_IGNORED; MMPort *port = NULL; + gulong send_delay = 5000; if (suggested_type == MM_PORT_TYPE_UNKNOWN) { if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) @@ -71,6 +76,9 @@ grab_port (MMModem *modem, mm_serial_parser_v1_e1_destroy); } + /* N900 appears to need longer delay between port bytes */ + g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, send_delay, NULL); + return !!port; } diff --git a/plugins/mm-modem-nokia.h b/plugins/mm-modem-nokia.h index 0e24619..0873d2d 100644 --- a/plugins/mm-modem-nokia.h +++ b/plugins/mm-modem-nokia.h @@ -38,6 +38,8 @@ GType mm_modem_nokia_get_type (void); MMModem *mm_modem_nokia_new (const char *data, const char *driver, - const char *plugin); + const char *plugin, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_NOKIA_H */ diff --git a/plugins/mm-modem-novatel-cdma.c b/plugins/mm-modem-novatel-cdma.c index 64ee15f..6b8c4aa 100644 --- a/plugins/mm-modem-novatel-cdma.c +++ b/plugins/mm-modem-novatel-cdma.c @@ -21,6 +21,8 @@ #include #include "mm-modem-novatel-cdma.h" +#include "mm-modem-helpers.h" +#include "libqcdm/src/commands.h" #include "mm-errors.h" #include "mm-callback-info.h" @@ -35,7 +37,9 @@ mm_modem_novatel_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA) + gboolean evdo_revA, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -72,6 +76,8 @@ get_one_qual (const char *reply, const char *tag) { int qual = -1; const char *p; + long int dbm; + gboolean success = FALSE; p = strstr (reply, tag); if (!p) @@ -83,17 +89,26 @@ get_one_qual (const char *reply, const char *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)); + + errno = 0; + dbm = strtol (p, NULL, 10); + if (errno == 0) { + if (*p == '-') { + /* Some cards appear to use RX0/RX1 and output RSSI in negative dBm */ + if (dbm < 0) + success = TRUE; + } else if (isdigit (*p) && (dbm > 0) && (dbm < 115)) { + /* S720 appears to use "1x RSSI" and print RSSI in dBm without '-' */ + dbm *= -1; + success = TRUE; } } + if (success) { + dbm = CLAMP (dbm, -113, -51); + qual = 100 - ((dbm + 51) * 100 / (-113 + 51)); + } + return qual; } @@ -122,6 +137,8 @@ get_rssi_done (MMAtSerialPort *port, /* Parse the signal quality */ qual = get_one_qual (response->str, "RX0="); + if (qual < 0) + qual = get_one_qual (response->str, "1x RSSI="); if (qual < 0) qual = get_one_qual (response->str, "RX1="); @@ -164,6 +181,115 @@ get_signal_quality (MMModemCdma *modem, /*****************************************************************************/ +static void +parse_modem_snapshot (MMCallbackInfo *info, QCDMResult *result) +{ + MMModemCdmaRegistrationState evdo_state, cdma1x_state, new_state; + guint8 eri = 0; + + g_return_if_fail (info != NULL); + g_return_if_fail (result != NULL); + + evdo_state = mm_generic_cdma_query_reg_state_get_callback_evdo_state (info); + cdma1x_state = mm_generic_cdma_query_reg_state_get_callback_1x_state (info); + + /* Roaming? */ + if (qcdm_result_get_uint8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_ERI, &eri)) { + char *str; + gboolean roaming = FALSE; + + str = g_strdup_printf ("%u", eri); + if (mm_cdma_parse_eri (str, &roaming, NULL, NULL)) { + new_state = roaming ? MM_MODEM_CDMA_REGISTRATION_STATE_HOME : MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; + if (cdma1x_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, new_state); + if (evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, new_state); + } + g_free (str); + } +} + +static void +reg_nwsnap_6500_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + QCDMResult *result; + + if (!error) { + result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result ((const char *) response->data, response->len, NULL); + if (result) { + parse_modem_snapshot (info, result); + qcdm_result_unref (result); + } + } + mm_callback_info_schedule (info); +} + +static void +reg_nwsnap_6800_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + QCDMResult *result; + GByteArray *nwsnap; + + if (error) + goto done; + + /* Parse the response */ + result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result ((const char *) response->data, response->len, &info->error); + if (!result) { + g_clear_error (&info->error); + + /* Try for MSM6500 */ + nwsnap = g_byte_array_sized_new (25); + nwsnap->len = qcdm_cmd_nw_subsys_modem_snapshot_cdma_new ((char *) nwsnap->data, 25, QCDM_NW_CHIPSET_6500, NULL); + g_assert (nwsnap->len); + mm_qcdm_serial_port_queue_command (port, nwsnap, 3, reg_nwsnap_6500_cb, info); + return; + } + + parse_modem_snapshot (info, result); + qcdm_result_unref (result); + +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; + MMQcdmSerialPort *port; + GByteArray *nwsnap; + + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, cur_cdma_state, cur_evdo_state, callback, user_data); + + port = mm_generic_cdma_get_best_qcdm_port (cdma, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* Try MSM6800 first since newer cards use that */ + nwsnap = g_byte_array_sized_new (25); + nwsnap->len = qcdm_cmd_nw_subsys_modem_snapshot_cdma_new ((char *) nwsnap->data, 25, QCDM_NW_CHIPSET_6800, NULL); + g_assert (nwsnap->len); + mm_qcdm_serial_port_queue_command (port, nwsnap, 3, reg_nwsnap_6800_cb, info); +} + +/*****************************************************************************/ + static void modem_cdma_init (MMModemCdma *cdma_class) { @@ -178,6 +304,10 @@ mm_modem_novatel_cdma_init (MMModemNovatelCdma *self) static void mm_modem_novatel_cdma_class_init (MMModemNovatelCdmaClass *klass) { + MMGenericCdmaClass *generic_class = MM_GENERIC_CDMA_CLASS (klass); + mm_modem_novatel_cdma_parent_class = g_type_class_peek_parent (klass); + + generic_class->query_registration_state = query_registration_state; } diff --git a/plugins/mm-modem-novatel-cdma.h b/plugins/mm-modem-novatel-cdma.h index 4d38d8e..c2319ef 100644 --- a/plugins/mm-modem-novatel-cdma.h +++ b/plugins/mm-modem-novatel-cdma.h @@ -40,6 +40,8 @@ MMModem *mm_modem_novatel_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA); + gboolean evdo_revA, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_NOVATEL_CDMA_H */ diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c index 584156f..64a81bd 100644 --- a/plugins/mm-modem-novatel-gsm.c +++ b/plugins/mm-modem-novatel-gsm.c @@ -33,7 +33,9 @@ G_DEFINE_TYPE_EXTENDED (MMModemNovatelGsm, mm_modem_novatel_gsm, MM_TYPE_GENERIC MMModem * mm_modem_novatel_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -43,6 +45,8 @@ mm_modem_novatel_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-novatel-gsm.h b/plugins/mm-modem-novatel-gsm.h index c2e1138..12b0060 100644 --- a/plugins/mm-modem-novatel-gsm.h +++ b/plugins/mm-modem-novatel-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_novatel_gsm_get_type (void); MMModem *mm_modem_novatel_gsm_new (const char *device, const char *driver, - const char *plugin_name); + const char *plugin_name, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_NOVATEL_GSM_H */ diff --git a/plugins/mm-modem-option.c b/plugins/mm-modem-option.c index ac04b0b..b15e704 100644 --- a/plugins/mm-modem-option.c +++ b/plugins/mm-modem-option.c @@ -36,7 +36,9 @@ typedef struct { MMModem * mm_modem_option_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -46,6 +48,8 @@ mm_modem_option_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-option.h b/plugins/mm-modem-option.h index 4e88607..3af9b1e 100644 --- a/plugins/mm-modem-option.h +++ b/plugins/mm-modem-option.h @@ -38,6 +38,8 @@ GType mm_modem_option_get_type (void); MMModem *mm_modem_option_new (const char *device, const char *driver, - const char *plugin_name); + const char *plugin_name, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_OPTION_H */ diff --git a/plugins/mm-modem-sierra-cdma.c b/plugins/mm-modem-sierra-cdma.c index fc62bf6..1168c83 100644 --- a/plugins/mm-modem-sierra-cdma.c +++ b/plugins/mm-modem-sierra-cdma.c @@ -51,7 +51,9 @@ mm_modem_sierra_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA) + gboolean evdo_revA, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -63,6 +65,8 @@ mm_modem_sierra_cdma_new (const char *device, MM_MODEM_PLUGIN, plugin, MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-sierra-cdma.h b/plugins/mm-modem-sierra-cdma.h index 9111b73..a33b1bc 100644 --- a/plugins/mm-modem-sierra-cdma.h +++ b/plugins/mm-modem-sierra-cdma.h @@ -40,6 +40,8 @@ MMModem *mm_modem_sierra_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA); + gboolean evdo_revA, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_SIERRA_CDMA_H */ diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c index bf5df31..d4636d7 100644 --- a/plugins/mm-modem-sierra-gsm.c +++ b/plugins/mm-modem-sierra-gsm.c @@ -15,30 +15,39 @@ */ #include +#include #include #include #include #include #include "mm-modem-sierra-gsm.h" #include "mm-errors.h" +#include "mm-modem-simple.h" #include "mm-callback-info.h" #include "mm-modem-helpers.h" static void modem_init (MMModem *modem_class); +static void modem_simple_init (MMModemSimple *class); G_DEFINE_TYPE_EXTENDED (MMModemSierraGsm, mm_modem_sierra_gsm, MM_TYPE_GENERIC_GSM, 0, - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_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; + gboolean has_net; + char *username; + char *password; } MMModemSierraGsmPrivate; MMModem * mm_modem_sierra_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -48,6 +57,8 @@ mm_modem_sierra_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -238,6 +249,85 @@ get_access_technology (MMGenericGsm *modem, mm_at_serial_port_queue_command (port, "*CNTI=0", 3, get_act_request_done, info); } +static void +get_sim_iccid_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + const char *p; + char buf[21]; + int i; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + p = mm_strip_tag (response->str, "!ICCID:"); + if (!p) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse !ICCID response"); + goto done; + } + + memset (buf, 0, sizeof (buf)); + for (i = 0; i < 20; i++) { + if (!isdigit (p[i]) && (p[i] != 'F') && (p[i] == 'f')) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "CRSM ICCID response contained invalid character '%c'", + p[i]); + goto done; + } + if (p[i] == 'F' || p[i] == 'f') { + buf[i] = 0; + break; + } + buf[i] = p[i]; + } + + if (i == 19 || i == 20) + mm_callback_info_set_result (info, g_strdup (buf), g_free); + else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Invalid +CRSM ICCID response size (was %d, expected 19 or 20)", + i); + } + +done: + mm_serial_port_close (MM_SERIAL_PORT (port)); + mm_callback_info_schedule (info); +} + +static void +get_sim_iccid (MMGenericGsm *modem, + MMModemStringFn callback, + gpointer callback_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + GError *error = NULL; + + port = mm_generic_gsm_get_best_at_port (modem, &error); + if (!port) + goto error; + + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &error)) + goto error; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, callback_data); + mm_at_serial_port_queue_command (port, "!ICCID?", 3, get_sim_iccid_done, info); + return; + +error: + callback (MM_MODEM (modem), NULL, error, callback_data); + g_clear_error (&error); +} + /*****************************************************************************/ /* Modem class override functions */ /*****************************************************************************/ @@ -302,25 +392,233 @@ grab_port (MMModem *modem, port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); - if (port && MM_IS_AT_SERIAL_PORT (port)) { - GRegex *regex; + if (port) { + if (MM_IS_AT_SERIAL_PORT (port)) { + GRegex *regex; - g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + 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); + 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); + } else if (mm_port_get_subsys (port) == MM_PORT_SUBSYS_NET) { + MM_MODEM_SIERRA_GSM_GET_PRIVATE (gsm)->has_net = TRUE; + g_object_set (G_OBJECT (gsm), MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP, NULL); + } } return !!port; } +static void +ppp_connect_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + info->error = mm_modem_check_removed (modem, error); + mm_callback_info_schedule (info); +} + +static void +net_activate_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), error, info); +} + + +static void +auth_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + gint cid; + char *command; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + return; + } + + cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (info->modem)); + g_warn_if_fail (cid >= 0); + + /* Activate data on the net port */ + command = g_strdup_printf ("!SCACT=1,%d", cid); + mm_at_serial_port_queue_command (port, command, 3, net_activate_done, info); + g_free (command); +} + +static void +ps_attach_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemSierraGsmPrivate *priv; + MMModem *parent_modem_iface; + const char *number; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + return; + } + + priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (info->modem); + if (priv->has_net) { + gint cid; + char *command; + + /* If we have a net interface, we don't do PPP */ + + cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (info->modem)); + g_warn_if_fail (cid >= 0); + + if (!priv->username || !priv->password) + command = g_strdup_printf ("$QCPDPP=%d,0", cid); + else { + command = g_strdup_printf ("$QCPDPP=%d,1,\"%s\",\"%s\"", + cid, + priv->password ? priv->password : "", + priv->username ? priv->username : ""); + + } + + mm_at_serial_port_queue_command (port, command, 3, auth_done, info); + g_free (command); + } else { + /* We've got a PS attach, chain up to parent for the connect */ + number = mm_callback_info_get_data (info, "number"); + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->connect (info->modem, number, ppp_connect_done, info); + } +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + info = mm_callback_info_new (modem, callback, user_data); + mm_callback_info_set_data (info, "number", g_strdup (number), g_free); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* Try to initiate a PS attach. Some Sierra modems can get into a + * state where if there is no PS attach when dialing, the next time + * the modem tries to connect it won't ever register with the network. + */ + mm_at_serial_port_queue_command (port, "+CGATT=1", 10, ps_attach_done, info); +} + +static void +clear_user_pass (MMModemSierraGsm *self) +{ + MMModemSierraGsmPrivate *priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (self); + + g_free (priv->username); + priv->username = NULL; + g_free (priv->password); + priv->username = NULL; +} + +static void +do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) +{ + clear_user_pass (MM_MODEM_SIERRA_GSM (gsm)); + + if (MM_MODEM_SIERRA_GSM_GET_PRIVATE (gsm)->has_net) { + MMAtSerialPort *primary; + char *command; + + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* If we have a net interface, deactivate it */ + command = g_strdup_printf ("!SCACT=0,%d", cid); + mm_at_serial_port_queue_command (primary, command, 3, NULL, NULL); + g_free (command); + } + + MM_GENERIC_GSM_CLASS (mm_modem_sierra_gsm_parent_class)->do_disconnect (gsm, cid, callback, user_data); +} + +/*****************************************************************************/ +/* Simple Modem class override functions */ +/*****************************************************************************/ + +static char * +simple_dup_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_dup_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, + MMModemFn callback, + gpointer user_data) +{ + MMModemSierraGsmPrivate *priv = MM_MODEM_SIERRA_GSM_GET_PRIVATE (simple); + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSimple *parent_iface; + + clear_user_pass (MM_MODEM_SIERRA_GSM (simple)); + priv->username = simple_dup_string_property (properties, "username", &info->error); + priv->password = simple_dup_string_property (properties, "password", &info->error); + + 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 modem_init (MMModem *modem_class) { modem_class->grab_port = grab_port; + modem_class->connect = do_connect; +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; } static void @@ -335,6 +633,8 @@ dispose (GObject *object) if (priv->enable_wait_id) g_source_remove (priv->enable_wait_id); + + clear_user_pass (MM_MODEM_SIERRA_GSM (object)); } static void @@ -351,5 +651,7 @@ mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass) gsm_class->set_allowed_mode = set_allowed_mode; gsm_class->get_allowed_mode = get_allowed_mode; gsm_class->get_access_technology = get_access_technology; + gsm_class->get_sim_iccid = get_sim_iccid; + gsm_class->do_disconnect = do_disconnect; } diff --git a/plugins/mm-modem-sierra-gsm.h b/plugins/mm-modem-sierra-gsm.h index dd84b30..8f2391d 100644 --- a/plugins/mm-modem-sierra-gsm.h +++ b/plugins/mm-modem-sierra-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_sierra_gsm_get_type (void); MMModem *mm_modem_sierra_gsm_new (const char *device, const char *driver, - const char *plugin_name); + const char *plugin_name, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_SIERRA_GSM_H */ diff --git a/plugins/mm-modem-simtech-gsm.c b/plugins/mm-modem-simtech-gsm.c index 07820b3..4a62694 100644 --- a/plugins/mm-modem-simtech-gsm.c +++ b/plugins/mm-modem-simtech-gsm.c @@ -34,7 +34,9 @@ G_DEFINE_TYPE_EXTENDED (MMModemSimtechGsm, mm_modem_simtech_gsm, MM_TYPE_GENERIC MMModem * mm_modem_simtech_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -44,6 +46,8 @@ mm_modem_simtech_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } diff --git a/plugins/mm-modem-simtech-gsm.h b/plugins/mm-modem-simtech-gsm.h index 0ba3c43..85e421c 100644 --- a/plugins/mm-modem-simtech-gsm.h +++ b/plugins/mm-modem-simtech-gsm.h @@ -38,6 +38,8 @@ GType mm_modem_simtech_gsm_get_type (void); MMModem *mm_modem_simtech_gsm_new (const char *device, const char *driver, - const char *plugin); + const char *plugin, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_SIMTECH_H */ diff --git a/plugins/mm-modem-x22x-gsm.c b/plugins/mm-modem-x22x-gsm.c new file mode 100644 index 0000000..ff23297 --- /dev/null +++ b/plugins/mm-modem-x22x-gsm.c @@ -0,0 +1,209 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include "mm-modem-x22x-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 (MMModemX22xGsm, mm_modem_x22x_gsm, MM_TYPE_GENERIC_GSM) + +MMModem * +mm_modem_x22x_gsm_new (const char *device, + const char *driver, + const char *plugin, + guint32 vendor, + guint32 product) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_X22X_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, + NULL)); +} + +/*****************************************************************************/ + +static gboolean +parse_syssel_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 ("\\+SYSSEL:\\s*(\\d),(\\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, 3); + 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_ANY; + 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 +get_allowed_mode_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemGsmAllowedMode mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + + info->error = mm_modem_check_removed (info->modem, error); + if (!info->error) { + parse_syssel_response (response, &mode, &info->error); + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + } + + 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, "+SYSSEL?", 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; + + info->error = mm_modem_check_removed (info->modem, 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 = NULL; + + 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: + command = "+SYSSEL=,1,0"; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + command = "+SYSSEL=,2,0"; + break; + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + default: + command = "+SYSSEL=,0,0"; + break; + } + + mm_at_serial_port_queue_command (port, command, 3, set_allowed_mode_done, info); +} + +/*****************************************************************************/ + +static void +mm_modem_x22x_gsm_init (MMModemX22xGsm *self) +{ +} + +static void +mm_modem_x22x_gsm_class_init (MMModemX22xGsmClass *klass) +{ + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_x22x_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; +} + diff --git a/plugins/mm-modem-x22x-gsm.h b/plugins/mm-modem-x22x-gsm.h new file mode 100644 index 0000000..61cc14d --- /dev/null +++ b/plugins/mm-modem-x22x-gsm.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_X22X_GSM_H +#define MM_MODEM_X22X_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_X22X_GSM (mm_modem_x22x_gsm_get_type ()) +#define MM_MODEM_X22X_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_X22X_GSM, MMModemX22xGsm)) +#define MM_MODEM_X22X_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_X22X_GSM, MMModemX22xGsmClass)) +#define MM_IS_MODEM_X22X_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_X22X_GSM)) +#define MM_IS_MODEM_X22X_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_X22X_GSM)) +#define MM_MODEM_X22X_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_X22X_GSM, MMModemX22xGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemX22xGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemX22xGsmClass; + +GType mm_modem_x22x_gsm_get_type (void); + +MMModem *mm_modem_x22x_gsm_new (const char *device, + const char *driver, + const char *plugin, + guint32 vendor, + guint32 product); + +#endif /* MM_MODEM_X22X_H */ diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index ba8a1db..c447c58 100644 --- a/plugins/mm-modem-zte.c +++ b/plugins/mm-modem-zte.c @@ -24,11 +24,17 @@ #include "mm-errors.h" #include "mm-callback-info.h" #include "mm-modem-helpers.h" +#include "mm-modem-simple.h" +#include "mm-modem-icera.h" static void modem_init (MMModem *modem_class); +static void modem_icera_init (MMModemIcera *icera_class); +static void modem_simple_init (MMModemSimple *simple_class); G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_ICERA, modem_icera_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) #define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate)) @@ -36,12 +42,15 @@ typedef struct { gboolean init_retried; guint32 cpms_tries; guint cpms_timeout; + gboolean is_icera; } MMModemZtePrivate; MMModem * mm_modem_zte_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint32 vendor, + guint32 product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -51,6 +60,8 @@ mm_modem_zte_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -145,9 +156,15 @@ get_allowed_mode (MMGenericGsm *gsm, MMModemUIntFn callback, gpointer user_data) { + MMModemZte *self = MM_MODEM_ZTE (gsm); MMCallbackInfo *info; MMAtSerialPort *port; + if (MM_MODEM_ZTE_GET_PRIVATE (self)->is_icera) { + mm_modem_icera_get_allowed_mode (MM_MODEM_ICERA (self), callback, user_data); + return; + } + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); port = mm_generic_gsm_get_best_at_port (gsm, &info->error); @@ -179,11 +196,17 @@ set_allowed_mode (MMGenericGsm *gsm, MMModemFn callback, gpointer user_data) { + MMModemZte *self = MM_MODEM_ZTE (gsm); MMCallbackInfo *info; MMAtSerialPort *port; char *command; int cm_mode = 0, pref_acq = 0; + if (MM_MODEM_ZTE_GET_PRIVATE (self)->is_icera) { + mm_modem_icera_set_allowed_mode (MM_MODEM_ICERA (self), mode, callback, user_data); + return; + } + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); port = mm_generic_gsm_get_best_at_port (gsm, &info->error); @@ -244,16 +267,22 @@ get_act_request_done (MMAtSerialPort *port, } static void -get_access_technology (MMGenericGsm *modem, +get_access_technology (MMGenericGsm *gsm, MMModemUIntFn callback, gpointer user_data) { + MMModemZte *self = MM_MODEM_ZTE (gsm); MMAtSerialPort *port; MMCallbackInfo *info; - info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + if (MM_MODEM_ZTE_GET_PRIVATE (self)->is_icera) { + mm_modem_icera_get_access_technology (MM_MODEM_ICERA (self), callback, user_data); + return; + } - port = mm_generic_gsm_get_best_at_port (modem, &info->error); + 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; @@ -262,6 +291,21 @@ get_access_technology (MMGenericGsm *modem, mm_at_serial_port_queue_command (port, "+ZPAS?", 3, get_act_request_done, info); } +static void +do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) +{ + MMModemZte *self = MM_MODEM_ZTE (gsm); + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); + + if (priv->is_icera) + mm_modem_icera_do_disconnect (gsm, cid, callback, user_data); + else + MM_GENERIC_GSM_CLASS (mm_modem_zte_parent_class)->do_disconnect (gsm, cid, callback, user_data); +} + /*****************************************************************************/ /* Modem class override functions */ /*****************************************************************************/ @@ -310,6 +354,10 @@ cpms_try_done (MMAtSerialPort *port, } } + /* Turn on unsolicited network state messages */ + if (priv->is_icera) + mm_modem_icera_change_unsolicited_messages (MM_MODEM_ICERA (info->modem), TRUE); + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); } @@ -321,6 +369,8 @@ init_modem_done (MMAtSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + mm_at_serial_port_queue_command (port, "E0", 5, NULL, NULL); + /* 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 @@ -333,6 +383,25 @@ static void enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data); +static void +icera_check_cb (MMModem *modem, + guint32 result, + GError *error, + gpointer user_data) +{ + if (!error) { + MMModemZte *self = MM_MODEM_ZTE (user_data); + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); + + if (result) { + priv->is_icera = TRUE; + g_object_set (G_OBJECT (modem), + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_STATIC, + NULL); + } + } +} + static void pre_init_done (MMAtSerialPort *port, GString *response, @@ -340,7 +409,8 @@ pre_init_done (MMAtSerialPort *port, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (info->modem); + MMModemZte *self = MM_MODEM_ZTE (info->modem); + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); if (error) { /* Retry the init string one more time; the modem sometimes throws it away */ @@ -349,9 +419,10 @@ pre_init_done (MMAtSerialPort *port, priv->init_retried = TRUE; enable_flash_done (MM_SERIAL_PORT (port), NULL, user_data); } else - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); } else { /* Finish the initialization */ + mm_modem_icera_is_icera (MM_MODEM_ICERA (self), icera_check_cb, self); mm_at_serial_port_queue_command (port, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1;", 10, init_modem_done, info); } } @@ -383,21 +454,112 @@ do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) mm_serial_port_flash (MM_SERIAL_PORT (primary), 100, FALSE, enable_flash_done, 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) { MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); - MMModem *parent_modem_iface; + MMAtSerialPort *primary; + DisableInfo *info; priv->init_retried = FALSE; - /* Do the normal disable stuff */ - parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); - parent_modem_iface->disable (modem, callback, user_data); + 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 */ + if (priv->is_icera) { + mm_modem_icera_cleanup (MM_MODEM_ICERA (modem)); + mm_modem_icera_change_unsolicited_messages (MM_MODEM_ICERA (modem), FALSE); + } + + /* Random command to ensure unsolicited message disable completes */ + mm_at_serial_port_queue_command (primary, "E0", 5, disable_unsolicited_done, info); +} + +/*****************************************************************************/ + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMModem *parent_iface; + + if (MM_MODEM_ZTE_GET_PRIVATE (modem)->is_icera) + mm_modem_icera_do_connect (MM_MODEM_ICERA (modem), number, callback, user_data); + else { + parent_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_iface->connect (MM_MODEM (modem), number, callback, user_data); + } +} + +static void +get_ip4_config (MMModem *modem, + MMModemIp4Fn callback, + gpointer user_data) +{ + MMModem *parent_iface; + + if (MM_MODEM_ZTE_GET_PRIVATE (modem)->is_icera) { + mm_modem_icera_get_ip4_config (MM_MODEM_ICERA (modem), callback, user_data); + } else { + parent_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_iface->get_ip4_config (MM_MODEM (modem), callback, user_data); + } +} + +/*****************************************************************************/ + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (simple); + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSimple *parent_iface; + + if (priv->is_icera) + mm_modem_icera_simple_connect (MM_MODEM_ICERA (simple), properties); + + parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); + parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); } +/*****************************************************************************/ + static gboolean grab_port (MMModem *modem, const char *subsys, @@ -447,6 +609,9 @@ grab_port (MMModem *modem, regex = g_regex_new ("\\r\\n\\+ZEND\\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); + + /* Add Icera-specific handlers */ + mm_modem_icera_register_unsolicted_handlers (MM_MODEM_ICERA (gsm), MM_AT_SERIAL_PORT (port)); } return !!port; @@ -458,9 +623,23 @@ static void modem_init (MMModem *modem_class) { modem_class->disable = disable; + modem_class->connect = do_connect; + modem_class->get_ip4_config = get_ip4_config; modem_class->grab_port = grab_port; } +static void +modem_icera_init (MMModemIcera *icera_class) +{ + mm_modem_icera_prepare (icera_class); +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + static void mm_modem_zte_init (MMModemZte *self) { @@ -474,6 +653,10 @@ dispose (GObject *object) if (priv->cpms_timeout) g_source_remove (priv->cpms_timeout); + + mm_modem_icera_cleanup (MM_MODEM_ICERA (self)); + + G_OBJECT_CLASS (mm_modem_zte_parent_class)->dispose (object); } static void @@ -487,6 +670,7 @@ mm_modem_zte_class_init (MMModemZteClass *klass) object_class->dispose = dispose; gsm_class->do_enable = do_enable; + gsm_class->do_disconnect = do_disconnect; 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-zte.h b/plugins/mm-modem-zte.h index 112bae0..f2f068b 100644 --- a/plugins/mm-modem-zte.h +++ b/plugins/mm-modem-zte.h @@ -38,6 +38,8 @@ GType mm_modem_zte_get_type (void); MMModem *mm_modem_zte_new (const char *device, const char *driver, - const char *plugin); + const char *plugin, + guint32 vendor, + guint32 product); #endif /* MM_MODEM_ZTE_H */ diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c index 94f4f10..76df673 100644 --- a/plugins/mm-plugin-anydata.c +++ b/plugins/mm-plugin-anydata.c @@ -110,6 +110,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -129,6 +130,11 @@ grab_port (MMPluginBase *base, return NULL; } + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & CAP_CDMA) { @@ -136,7 +142,9 @@ grab_port (MMPluginBase *base, 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c index cdf2c66..a27704b 100644 --- a/plugins/mm-plugin-generic.c +++ b/plugins/mm-plugin-generic.c @@ -10,7 +10,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details: * - * Copyright (C) 2008 - 2009 Dan Williams + * Copyright (C) 2008 - 2010 Dan Williams */ #include @@ -31,6 +31,7 @@ #include "mm-generic-cdma.h" #include "mm-errors.h" #include "mm-serial-parsers.h" +#include "mm-log.h" G_DEFINE_TYPE (MMPluginGeneric, mm_plugin_generic, MM_TYPE_PLUGIN_BASE) @@ -112,6 +113,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path, *driver; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -126,26 +128,35 @@ grab_port (MMPluginBase *base, g_set_error (error, 0, 0, "Could not get port's sysfs file."); return NULL; } else { - g_message ("%s: (%s/%s) WARNING: missing udev 'device' file", - mm_plugin_get_name (MM_PLUGIN (base)), - subsys, - name); + mm_warn ("%s: (%s/%s) WARNING: missing udev 'device' file", + mm_plugin_get_name (MM_PLUGIN (base)), + subsys, + name); } } + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { - if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { - modem = mm_generic_gsm_new (sysfs_path, - mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); - } else if (caps & CAP_CDMA) { + 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); + } else 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)), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c index fbe3878..36771d1 100644 --- a/plugins/mm-plugin-gobi.c +++ b/plugins/mm-plugin-gobi.c @@ -106,6 +106,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -113,19 +114,28 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_gobi_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c index dc0a8fc..7b71932 100644 --- a/plugins/mm-plugin-hso.c +++ b/plugins/mm-plugin-hso.c @@ -105,6 +105,7 @@ grab_port (MMPluginBase *base, const char *name, *subsys, *sysfs_path; char *devfile; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -112,6 +113,11 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + devfile = g_strdup (g_udev_device_get_device_file (port)); if (!devfile) { if (!strcmp (subsys, "net")) { @@ -139,7 +145,9 @@ grab_port (MMPluginBase *base, if (!existing) { modem = mm_modem_hso_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); if (modem) { if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { g_object_unref (modem); diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c index 2993689..f1590ca 100644 --- a/plugins/mm-plugin-huawei.c +++ b/plugins/mm-plugin-huawei.c @@ -27,6 +27,7 @@ #include "mm-modem-huawei-cdma.h" #include "mm-serial-parsers.h" #include "mm-at-serial-port.h" +#include "mm-log.h" G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE) @@ -239,19 +240,19 @@ supports_port (MMPluginBase *base, 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 (MM_SERIAL_PORT (info->serial), &error)) { - g_warning ("%s: (Huawei) %s: couldn't open serial port: (%d) %s", - __func__, name, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); + mm_warn ("(Huawei) %s: couldn't open serial port: (%d) %s", + name, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); g_clear_error (&error); huawei_supports_info_destroy (info); return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } + g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO, + info, huawei_supports_info_destroy); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; } @@ -268,7 +269,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; - guint16 product = 0; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -282,7 +283,7 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); - if (!mm_plugin_base_get_device_ids (base, subsys, name, NULL, &product)) { + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { g_set_error (error, 0, 0, "Could not get modem product ID."); return NULL; } @@ -292,14 +293,18 @@ grab_port (MMPluginBase *base, if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_huawei_gsm_new (sysfs_path, - mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } else if (caps & CAP_CDMA) { modem = mm_modem_huawei_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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-linktop.c b/plugins/mm-plugin-linktop.c new file mode 100644 index 0000000..411f9cc --- /dev/null +++ b/plugins/mm-plugin-linktop.c @@ -0,0 +1,164 @@ +/* -*- 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. + */ + +#include +#include +#include "mm-plugin-linktop.h" +#include "mm-modem-linktop.h" + +G_DEFINE_TYPE (MMPluginLinktop, mm_plugin_linktop, 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_LINKTOP, + MM_PLUGIN_BASE_NAME, "Linktop", + NULL)); +} + +/*****************************************************************************/ + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x230d) + 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, *devfile, *sysfs_path; + guint32 caps; + guint16 vendor = 0, product = 0; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_linktop_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else if (get_level_for_capabilities (caps)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_linktop_init (MMPluginLinktop *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_linktop_class_init (MMPluginLinktopClass *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-linktop.h b/plugins/mm-plugin-linktop.h new file mode 100644 index 0000000..b904725 --- /dev/null +++ b/plugins/mm-plugin-linktop.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 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_LINKTOP_H +#define MM_PLUGIN_LINKTOP_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_LINKTOP (mm_plugin_linktop_get_type ()) +#define MM_PLUGIN_LINKTOP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_LINKTOP, MMPluginLinktop)) +#define MM_PLUGIN_LINKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_LINKTOP, MMPluginLinktopClass)) +#define MM_IS_PLUGIN_LINKTOP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_LINKTOP)) +#define MM_IS_PLUGIN_LINKTOP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_LINKTOP)) +#define MM_PLUGIN_LINKTOP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_LINKTOP, MMPluginLinktopClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginLinktop; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginLinktopClass; + +GType mm_plugin_linktop_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_LINKTOP_H */ diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c index dbbe186..e63ff4e 100644 --- a/plugins/mm-plugin-longcheer.c +++ b/plugins/mm-plugin-longcheer.c @@ -114,6 +114,7 @@ grab_port (MMPluginBase *base, const char *name, *subsys, *sysfs_path; guint32 caps; MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -138,19 +139,28 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_longcheer_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c index 5554d84..6684ff9 100644 --- a/plugins/mm-plugin-mbm.c +++ b/plugins/mm-plugin-mbm.c @@ -84,7 +84,7 @@ supports_port (MMPluginBase *base, client = g_udev_client_new (sys); if (!client) { - g_warning ("mbm: could not get udev client."); + g_warn_if_fail (client != NULL); return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } @@ -132,6 +132,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *sysfs_path; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -143,11 +144,18 @@ grab_port (MMPluginBase *base, if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net")) return NULL; + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + 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), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); if (modem) { if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { g_object_unref (modem); diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c index d798af4..21b737e 100644 --- a/plugins/mm-plugin-moto-c.c +++ b/plugins/mm-plugin-moto-c.c @@ -105,6 +105,7 @@ grab_port (MMPluginBase *base, GUdevDevice *port = NULL; MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -118,11 +119,18 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + 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), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); if (modem) { if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c index 2d0d6af..938f8c5 100644 --- a/plugins/mm-plugin-nokia.c +++ b/plugins/mm-plugin-nokia.c @@ -109,6 +109,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -122,19 +123,28 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_nokia_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c index a968836..5384456 100644 --- a/plugins/mm-plugin-novatel.c +++ b/plugins/mm-plugin-novatel.c @@ -115,6 +115,7 @@ grab_port (MMPluginBase *base, MMModem *modem = NULL; const char *name, *subsys, *devfile, *sysfs_path; guint32 caps; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -128,19 +129,28 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_novatel_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } else if (caps & CAP_CDMA) { 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c index 101f9bd..ab2bd3d 100644 --- a/plugins/mm-plugin-option.c +++ b/plugins/mm-plugin-option.c @@ -107,6 +107,7 @@ grab_port (MMPluginBase *base, guint32 caps; int usbif; MMPortType ptype = MM_PORT_TYPE_SECONDARY; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -129,13 +130,20 @@ grab_port (MMPluginBase *base, if (usbif == 0) ptype = MM_PORT_TYPE_PRIMARY; + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_option_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c index 8ace653..b7367ab 100644 --- a/plugins/mm-plugin-sierra.c +++ b/plugins/mm-plugin-sierra.c @@ -90,17 +90,28 @@ supports_port (MMPluginBase *base, { GUdevDevice *port; guint32 cached = 0, level; - const char *driver; + const char *driver, *subsys; /* 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")) + if (!port) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; driver = mm_plugin_base_supports_task_get_driver (task); - if (!driver || strcmp (driver, "sierra")) + if (!driver || (strcmp (driver, "sierra") && strcmp (driver, "sierra_net"))) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + if (!strcmp (subsys, "net")) { + /* Can't grab the net port until we know whether this is a CDMA or GSM device */ + if (!existing) + return MM_PLUGIN_SUPPORTS_PORT_DEFER; + + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { level = get_level_for_capabilities (cached); if (level) { @@ -125,19 +136,14 @@ grab_port (MMPluginBase *base, { GUdevDevice *port = NULL; MMModem *modem = NULL; - const char *name, *subsys, *devfile, *sysfs_path; + const char *name, *subsys, *sysfs_path; guint32 caps; MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); - devfile = g_udev_device_get_device_file (port); - if (!devfile) { - g_set_error (error, 0, 0, "Could not get port's sysfs file."); - return NULL; - } - subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); @@ -145,19 +151,28 @@ grab_port (MMPluginBase *base, if (g_object_get_data (G_OBJECT (task), TAG_SIERRA_SECONDARY_PORT)) ptype = MM_PORT_TYPE_SECONDARY; + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if ((caps & MM_PLUGIN_BASE_PORT_CAP_GSM) || (ptype != MM_PORT_TYPE_UNKNOWN)) { modem = mm_modem_sierra_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } else if (caps & CAP_CDMA) { modem = mm_modem_sierra_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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { @@ -166,7 +181,16 @@ grab_port (MMPluginBase *base, return NULL; } } - } else if (get_level_for_capabilities (caps) || (ptype != MM_PORT_TYPE_UNKNOWN)) { + } else if ( get_level_for_capabilities (caps) + || (ptype != MM_PORT_TYPE_UNKNOWN) + || (strcmp (subsys, "net") == 0)) { + + /* FIXME: we don't yet know how to activate IP on CDMA devices using + * pseudo-ethernet ports. + */ + if (strcmp (subsys, "net") == 0 && MM_IS_MODEM_SIERRA_CDMA (modem)) + return modem; + modem = existing; if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) return NULL; diff --git a/plugins/mm-plugin-simtech.c b/plugins/mm-plugin-simtech.c index 3c44873..611d2d2 100644 --- a/plugins/mm-plugin-simtech.c +++ b/plugins/mm-plugin-simtech.c @@ -114,6 +114,7 @@ grab_port (MMPluginBase *base, const char *name, *subsys, *sysfs_path; guint32 caps; MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -138,19 +139,28 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_simtech_gsm_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { diff --git a/plugins/mm-plugin-x22x.c b/plugins/mm-plugin-x22x.c new file mode 100644 index 0000000..5a8253f --- /dev/null +++ b/plugins/mm-plugin-x22x.c @@ -0,0 +1,192 @@ +/* -*- 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 +#include +#include "mm-plugin-x22x.h" +#include "mm-modem-x22x-gsm.h" +#include "mm-generic-gsm.h" + +G_DEFINE_TYPE (MMPluginX22x, mm_plugin_x22x, 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_X22X, + MM_PLUGIN_BASE_NAME, "X22X", + NULL)); +} + +/*****************************************************************************/ + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + /* These modems have the same vendor ID as a few of the other + * Alcatel/TCT/T&A modems, and the longcheer plugin will try to claim them + * too. So we provide a higher support level here to make sure this + * plugin tries to grab it's supported devices first. + */ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 20; + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_QCDM) + return 20; + 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; + + /* Only TCT/T&A for now */ + if (vendor != 0x1bbb) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* If the port wasn't tagged, we don't support it */ + if (!g_udev_device_get_property_as_boolean (port, "ID_MM_X22X_TAGGED")) + 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; + guint16 vendor = 0, product = 0; + + 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_X22X_PORT_TYPE_MODEM")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_udev_device_get_property_as_boolean (port, "ID_MM_X22X_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_X22X_TAGGED")) + ptype = MM_PORT_TYPE_IGNORED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_x22x_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, 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_x22x_init (MMPluginX22x *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_x22x_class_init (MMPluginX22xClass *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-x22x.h b/plugins/mm-plugin-x22x.h new file mode 100644 index 0000000..227511d --- /dev/null +++ b/plugins/mm-plugin-x22x.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_X22X_H +#define MM_PLUGIN_X22X_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_X22X (mm_plugin_x22x_get_type ()) +#define MM_PLUGIN_X22X(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_X22X, MMPluginX22x)) +#define MM_PLUGIN_X22X_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_X22X, MMPluginX22xClass)) +#define MM_IS_PLUGIN_X22X(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_X22X)) +#define MM_IS_PLUGIN_X22X_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_X22X)) +#define MM_PLUGIN_X22X_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_X22X, MMPluginX22xClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginX22x; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginX22xClass; + +GType mm_plugin_x22x_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_X22X_H */ diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c index e943bbf..bb2ee17 100644 --- a/plugins/mm-plugin-zte.c +++ b/plugins/mm-plugin-zte.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. */ #include @@ -73,9 +73,6 @@ supports_port (MMPluginBase *base, /* 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); @@ -85,6 +82,20 @@ supports_port (MMPluginBase *base, if (vendor != 0x19d2) return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + if (!strcmp (subsys, "net")) { + /* If we don't know the modem's type yet, defer grabbing the port + * until we know the type. + */ + if (!existing) + return MM_PLUGIN_SUPPORTS_PORT_DEFER; + + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + if (strcmp (subsys, "tty")) + 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) { @@ -121,6 +132,7 @@ grab_port (MMPluginBase *base, const char *name, *subsys, *sysfs_path; guint32 caps; MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + guint16 vendor = 0, product = 0; port = mm_plugin_base_supports_task_get_port (task); g_assert (port); @@ -134,19 +146,28 @@ grab_port (MMPluginBase *base, subsys = g_udev_device_get_subsystem (port); name = g_udev_device_get_name (port); + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); if (!existing) { if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { modem = mm_modem_zte_new (sysfs_path, mm_plugin_base_supports_task_get_driver (task), - mm_plugin_get_name (MM_PLUGIN (base))); + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); } 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)); + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A), + vendor, + product); } if (modem) { @@ -155,7 +176,7 @@ grab_port (MMPluginBase *base, return NULL; } } - } else if (get_level_for_capabilities (caps)) { + } else if (get_level_for_capabilities (caps) || (!strcmp (subsys, "net"))) { if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) ptype = MM_PORT_TYPE_QCDM; diff --git a/policy/org.freedesktop.modem-manager.policy.in b/policy/org.freedesktop.modem-manager.policy.in index 8719936..ed98f63 100644 --- a/policy/org.freedesktop.modem-manager.policy.in +++ b/policy/org.freedesktop.modem-manager.policy.in @@ -54,4 +54,13 @@ + + <_description>Query and utilize network information and services + <_message>System policy prevents querying or utilizing network information and services. + + no + yes + + + diff --git a/src/77-mm-usb-device-blacklist.rules b/src/77-mm-usb-device-blacklist.rules index 78a6770..58b0cee 100644 --- a/src/77-mm-usb-device-blacklist.rules +++ b/src/77-mm-usb-device-blacklist.rules @@ -62,5 +62,11 @@ ATTRS{idVendor}=="0592", ATTRS{idProduct}=="0002", ENV{ID_MM_DEVICE_IGNORE}="1" # that isn't blacklisted. ATTRS{idVendor}=="0830", ATTRS{idProduct}=="0061", ENV{ID_MM_DEVICE_IGNORE}="1" +# Belkin F5U183 Serial Adapter (unlikely to have a modem behind it) +ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0103", ENV{ID_MM_DEVICE_IGNORE}="1" + +# ATEN Intl UC-232A (Prolific) +ATTRS{idVendor}=="0557", ATTRS{idProduct}=="2008", ENV{ID_MM_DEVICE_IGNORE}="1" + LABEL="mm_usb_device_blacklist_end" diff --git a/src/80-mm-candidate.rules b/src/80-mm-candidate.rules new file mode 100644 index 0000000..e99ae3a --- /dev/null +++ b/src/80-mm-candidate.rules @@ -0,0 +1,16 @@ +# do not edit this file, it will be overwritten on update + +# Tag any devices that MM might be interested in; if ModemManager is started +# up right after udev, when MM explicitly requests devices on startup it may +# get devices that haven't had all rules run yet. Thus, we tag devices we're +# interested in and when handling devices during MM startup we ignore any +# that don't have this tag. MM will still get the udev 'add' event for the +# device a short while later and then process it as normal. + +ACTION!="add|change|move", GOTO="mm_candidate_end" + +SUBSYSTEM=="tty", ENV{ID_MM_CANDIDATE}="1" +SUBSYSTEM=="net", ENV{ID_MM_CANDIDATE}="1" + +LABEL="mm_candidate_end" + diff --git a/src/Makefile.am b/src/Makefile.am index 2061ae8..e813e7e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,12 +4,13 @@ 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 + 77-mm-platform-serial-whitelist.rules \ + 80-mm-candidate.rules EXTRA_DIST = \ $(udevrules_DATA) -noinst_LTLIBRARIES = libmodem-helpers.la +noinst_LTLIBRARIES = libmodem-helpers.la libserial.la libmodem_helpers_la_CPPFLAGS = \ $(MM_CFLAGS) @@ -24,6 +25,20 @@ libmodem_helpers_la_SOURCES = \ mm-utils.c \ mm-utils.h +libserial_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + -I$(top_srcdir) + +libserial_la_SOURCES = \ + mm-port.c \ + mm-port.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 + sbin_PROGRAMS = modem-manager modem_manager_CPPFLAGS = \ @@ -41,8 +56,9 @@ modem_manager_LDADD = \ $(MM_LIBS) \ $(GUDEV_LIBS) \ $(top_builddir)/marshallers/libmarshallers.la \ - $(top_builddir)/libqcdm/src/libqcdm.la \ - $(builddir)/libmodem-helpers.la + $(builddir)/libmodem-helpers.la \ + $(builddir)/libserial.la \ + $(top_builddir)/libqcdm/src/libqcdm.la if WITH_POLKIT modem_manager_LDADD += $(POLKIT_LIBS) @@ -63,12 +79,10 @@ auth_sources += \ mm-auth-provider-polkit.h endif -loc_sources = \ - mm-modem-location.c \ - mm-modem-location.h - modem_manager_SOURCES = \ main.c \ + mm-log.c \ + mm-log.h \ mm-callback-info.c \ mm-callback-info.h \ $(auth_sources) \ @@ -76,14 +90,6 @@ modem_manager_SOURCES = \ mm-manager.h \ mm-modem.c \ mm-modem.h \ - mm-port.c \ - mm-port.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 \ @@ -101,37 +107,45 @@ modem_manager_SOURCES = \ mm-modem-gsm-network.h \ mm-modem-gsm-sms.c \ mm-modem-gsm-sms.h \ + mm-modem-gsm-ussd.c \ + mm-modem-gsm-ussd.h \ mm-modem-simple.c \ mm-modem-simple.h \ - mm-options.c \ - mm-options.h \ mm-plugin.c \ mm-plugin.h \ mm-plugin-base.c \ mm-plugin-base.h \ mm-properties-changed-signal.c \ - mm-properties-changed-signal.h + mm-properties-changed-signal.h \ + mm-modem-location.c \ + mm-modem-location.h -mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml - dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< +mm-manager-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $< -mm-modem-glue.h: $(top_srcdir)/introspection/mm-modem.xml - dbus-binding-tool --prefix=mm_modem --mode=glib-server --output=$@ $< +mm-modem-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem --mode=glib-server --output=$@ $< -mm-modem-simple-glue.h: $(top_srcdir)/introspection/mm-modem-simple.xml - dbus-binding-tool --prefix=mm_modem_simple --mode=glib-server --output=$@ $< +mm-modem-simple-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Simple.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_simple --mode=glib-server --output=$@ $< -mm-modem-cdma-glue.h: $(top_srcdir)/introspection/mm-modem-cdma.xml - dbus-binding-tool --prefix=mm_modem_cdma --mode=glib-server --output=$@ $< +mm-modem-cdma-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Cdma.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_cdma --mode=glib-server --output=$@ $< -mm-modem-gsm-card-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-card.xml - dbus-binding-tool --prefix=mm_modem_gsm_card --mode=glib-server --output=$@ $< +mm-modem-gsm-card-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Gsm.Card.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_card --mode=glib-server --output=$@ $< -mm-modem-gsm-network-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-network.xml - dbus-binding-tool --prefix=mm_modem_gsm_network --mode=glib-server --output=$@ $< +mm-modem-gsm-network-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Gsm.Network.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_network --mode=glib-server --output=$@ $< -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=$@ $< +mm-modem-gsm-sms-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Gsm.SMS.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_sms --mode=glib-server --output=$@ $< + +mm-modem-gsm-ussd-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Gsm.Ussd.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_ussd --mode=glib-server --output=$@ $< + +mm-properties-changed-glue.h: $(top_srcdir)/introspection/org.freedesktop.DBus.Properties.xml + $(AM_V_GEN) dbus-binding-tool --prefix=mm_properties_changed --mode=glib-server --output=$@ $< BUILT_SOURCES = \ mm-manager-glue.h \ @@ -140,17 +154,15 @@ BUILT_SOURCES = \ mm-modem-cdma-glue.h \ mm-modem-gsm-card-glue.h \ mm-modem-gsm-network-glue.h \ - mm-modem-gsm-sms-glue.h + mm-modem-gsm-sms-glue.h \ + mm-modem-gsm-ussd-glue.h \ + mm-properties-changed-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=$@ $< +mm-modem-location-glue.h: $(top_srcdir)/introspection/org.freedesktop.ModemManager.Modem.Location.xml + $(AM_V_GEN) 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 72fa6dc..0f8119e 100644 --- a/src/main.c +++ b/src/main.c @@ -11,7 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 - 2010 Red Hat, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. */ #include @@ -21,8 +21,10 @@ #include #include #include +#include + #include "mm-manager.h" -#include "mm-options.h" +#include "mm-log.h" #if !defined(MM_DIST_VERSION) # define MM_DIST_VERSION VERSION @@ -34,9 +36,9 @@ static void mm_signal_handler (int signo) { if (signo == SIGUSR1) - mm_options_set_debug (!mm_options_debug ()); + mm_log_usr1 (); else if (signo == SIGINT || signo == SIGTERM) { - g_message ("Caught signal %d, shutting down...", signo); + mm_info ("Caught signal %d, shutting down...", signo); if (loop) g_main_loop_quit (loop); else @@ -59,65 +61,10 @@ setup_signals (void) sigaction (SIGINT, &action, NULL); } -static void -log_handler (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer ignored) -{ - int syslog_priority; - - switch (log_level) { - case G_LOG_LEVEL_ERROR: - syslog_priority = LOG_CRIT; - break; - - case G_LOG_LEVEL_CRITICAL: - syslog_priority = LOG_ERR; - break; - - case G_LOG_LEVEL_WARNING: - syslog_priority = LOG_WARNING; - break; - - case G_LOG_LEVEL_MESSAGE: - syslog_priority = LOG_NOTICE; - break; - - case G_LOG_LEVEL_DEBUG: - syslog_priority = LOG_DEBUG; - break; - - case G_LOG_LEVEL_INFO: - default: - syslog_priority = LOG_INFO; - break; - } - - syslog (syslog_priority, "%s", message); -} - - -static void -logging_setup (void) -{ - openlog (G_LOG_DOMAIN, LOG_CONS, LOG_DAEMON); - g_log_set_handler (G_LOG_DOMAIN, - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, - log_handler, - NULL); -} - -static void -logging_shutdown (void) -{ - closelog (); -} - static void destroy_cb (DBusGProxy *proxy, gpointer user_data) { - g_message ("disconnected from the system bus, exiting."); + mm_warn ("disconnected from the system bus, exiting."); g_main_loop_quit (loop); } @@ -139,16 +86,16 @@ create_dbus_proxy (DBusGConnection *bus) G_TYPE_INVALID, G_TYPE_UINT, &request_name_result, G_TYPE_INVALID)) { - g_warning ("Could not acquire the %s service.\n" - " Message: '%s'", MM_DBUS_SERVICE, err->message); + mm_warn ("Could not acquire the %s service.\n" + " Message: '%s'", MM_DBUS_SERVICE, err->message); g_error_free (err); g_object_unref (proxy); proxy = NULL; } else if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - g_warning ("Could not acquire the " MM_DBUS_SERVICE - " service as it is already taken. Return: %d", - request_name_result); + mm_warn ("Could not acquire the " MM_DBUS_SERVICE + " service as it is already taken. Return: %d", + request_name_result); g_object_unref (proxy); proxy = NULL; @@ -175,17 +122,49 @@ main (int argc, char *argv[]) DBusGProxy *proxy; MMManager *manager; GError *err = NULL; + GOptionContext *opt_ctx; guint id; + const char *log_level = NULL, *log_file = NULL; + gboolean debug = FALSE, show_ts = FALSE, rel_ts = FALSE; + + GOptionEntry entries[] = { + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, + { "log-level", 0, 0, G_OPTION_ARG_STRING, &log_level, "Log level: one of [ERR, WARN, INFO, DEBUG]", "INFO" }, + { "log-file", 0, 0, G_OPTION_ARG_STRING, &log_file, "Path to log file", NULL }, + { "timestamps", 0, 0, G_OPTION_ARG_NONE, &show_ts, "Show timestamps in log output", NULL }, + { "relative-timestamps", 0, 0, G_OPTION_ARG_NONE, &rel_ts, "Use relative timestamps (from MM start)", NULL }, + { NULL } + }; - mm_options_parse (argc, argv); g_type_init (); - setup_signals (); + opt_ctx = g_option_context_new (NULL); + g_option_context_set_summary (opt_ctx, "DBus system service to communicate with modems."); + g_option_context_add_main_entries (opt_ctx, entries, NULL); - if (!mm_options_debug ()) - logging_setup (); + if (!g_option_context_parse (opt_ctx, &argc, &argv, &err)) { + g_warning ("%s\n", err->message); + g_error_free (err); + exit (1); + } - g_message ("ModemManager (version " MM_DIST_VERSION ") starting..."); + g_option_context_free (opt_ctx); + + if (debug) { + log_level = "DEBUG"; + if (!show_ts && !rel_ts) + show_ts = TRUE; + } + + if (!mm_log_setup (log_level, log_file, show_ts, rel_ts, &err)) { + g_warning ("Failed to set up logging: %s", err->message); + g_error_free (err); + exit (1); + } + + setup_signals (); + + mm_info ("ModemManager (version " MM_DIST_VERSION ") starting..."); bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); if (!bus) { @@ -196,6 +175,17 @@ main (int argc, char *argv[]) return -1; } +#ifndef HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS +#error HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS not defined +#endif + +#if HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS + /* Ensure that non-exported properties don't leak out, and that the + * introspection 'access' permissions are respected. + */ + dbus_glib_global_set_disable_legacy_property_access (); +#endif + proxy = create_dbus_proxy (bus); if (!proxy) return -1; @@ -224,7 +214,7 @@ main (int argc, char *argv[]) g_object_unref (proxy); dbus_g_connection_unref (bus); - logging_shutdown (); + mm_log_shutdown (); return 0; } diff --git a/src/mm-at-serial-port.c b/src/mm-at-serial-port.c index 068450d..30da3a3 100644 --- a/src/mm-at-serial-port.c +++ b/src/mm-at-serial-port.c @@ -23,7 +23,7 @@ #include "mm-at-serial-port.h" #include "mm-errors.h" -#include "mm-options.h" +#include "mm-log.h" G_DEFINE_TYPE (MMAtSerialPort, mm_at_serial_port, MM_TYPE_SERIAL_PORT) @@ -273,7 +273,6 @@ 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); @@ -290,18 +289,13 @@ debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len) else if (*s == '\n') g_string_append (debug, ""); else - g_string_append_printf (debug, "\\%d", *s); + g_string_append_printf (debug, "\\%u", (guint8) (*s & 0xFF)); 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); + mm_dbg ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str); g_string_truncate (debug, 0); } diff --git a/src/mm-auth-provider-polkit.c b/src/mm-auth-provider-polkit.c index c457eaf..2cd4a8a 100644 --- a/src/mm-auth-provider-polkit.c +++ b/src/mm-auth-provider-polkit.c @@ -15,6 +15,7 @@ #include +#include #include "mm-auth-request-polkit.h" #include "mm-auth-provider-polkit.h" @@ -72,19 +73,39 @@ real_create_request (MMAuthProvider *provider, /*****************************************************************************/ +/* Fix for polkit 0.97 and later */ +#if !HAVE_POLKIT_AUTHORITY_GET_SYNC +static inline PolkitAuthority * +polkit_authority_get_sync (GCancellable *cancellable, GError **error) +{ + PolkitAuthority *authority; + + authority = polkit_authority_get (); + if (!authority) + g_set_error (error, 0, 0, "failed to get the PolicyKit authority"); + return authority; +} +#endif + static void mm_auth_provider_polkit_init (MMAuthProviderPolkit *self) { MMAuthProviderPolkitPrivate *priv = MM_AUTH_PROVIDER_POLKIT_GET_PRIVATE (self); + GError *error = NULL; - priv->authority = polkit_authority_get (); + priv->authority = polkit_authority_get_sync (NULL, &error); 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__); + } else { + g_warning ("%s: failed to create PolicyKit authority: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } } static void diff --git a/src/mm-auth-provider.h b/src/mm-auth-provider.h index 26ff340..32082f0 100644 --- a/src/mm-auth-provider.h +++ b/src/mm-auth-provider.h @@ -26,6 +26,7 @@ #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_USSD "org.freedesktop.ModemManager.USSD" #define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager.Location" /******************/ diff --git a/src/mm-charsets.c b/src/mm-charsets.c index c75c3a9..d2b9a66 100644 --- a/src/mm-charsets.c +++ b/src/mm-charsets.c @@ -109,7 +109,7 @@ charset_iconv_from (MMModemCharset charset) gboolean mm_modem_charset_byte_array_append (GByteArray *array, - const char *string, + const char *utf8, gboolean quoted, MMModemCharset charset) { @@ -119,22 +119,16 @@ mm_modem_charset_byte_array_append (GByteArray *array, gsize written = 0; g_return_val_if_fail (array != NULL, FALSE); - g_return_val_if_fail (string != NULL, FALSE); + g_return_val_if_fail (utf8 != 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); + converted = g_convert (utf8, -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, + __func__, utf8, iconv_to, error->code, error->message); g_error_free (error); } @@ -154,9 +148,10 @@ mm_modem_charset_byte_array_append (GByteArray *array, char * mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset) { - char *unconverted; + char *unconverted, *converted; const char *iconv_from; gsize unconverted_len = 0; + GError *error = NULL; g_return_val_if_fail (src != NULL, NULL); g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL); @@ -170,6 +165,340 @@ mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset) 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); + converted = g_convert (unconverted, unconverted_len, + "UTF-8//TRANSLIT", iconv_from, + NULL, NULL, &error); + if (!converted || error) { + g_clear_error (&error); + g_free (unconverted); + converted = NULL; + } + + return converted; +} + + +/* GSM 03.38 encoding conversion stuff */ + +#define GSM_DEF_ALPHABET_SIZE 128 +#define GSM_EXT_ALPHABET_SIZE 10 + +typedef struct GsmUtf8Mapping { + gchar chars[3]; + guint8 len; + guint8 gsm; /* only used for extended GSM charset */ +} GsmUtf8Mapping; + +#define ONE(a) { {a, 0x00, 0x00}, 1, 0 } +#define TWO(a, b) { {a, b, 0x00}, 2, 0 } + +/** + * gsm_def_utf8_alphabet: + * + * Mapping from GSM default alphabet to UTF-8. + * + * ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet. Mapping to UCS-2. + * Mapping according to http://unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT + */ +static const GsmUtf8Mapping gsm_def_utf8_alphabet[GSM_DEF_ALPHABET_SIZE] = { + /* @ £ $ ¥ */ + ONE(0x40), TWO(0xc2, 0xa3), ONE(0x24), TWO(0xc2, 0xa5), + /* è é ù ì */ + TWO(0xc3, 0xa8), TWO(0xc3, 0xa9), TWO(0xc3, 0xb9), TWO(0xc3, 0xac), + /* ò Ç \n Ø */ + TWO(0xc3, 0xb2), TWO(0xc3, 0x87), ONE(0x0a), TWO(0xc3, 0x98), + /* ø \r Å å */ + TWO(0xc3, 0xb8), ONE(0x0d), TWO(0xc3, 0x85), TWO(0xc3, 0xa5), + /* Δ _ Φ Γ */ + TWO(0xce, 0x94), ONE(0x5f), TWO(0xce, 0xa6), TWO(0xce, 0x93), + /* Λ Ω Π Ψ */ + TWO(0xce, 0x9b), TWO(0xce, 0xa9), TWO(0xce, 0xa0), TWO(0xce, 0xa8), + /* Σ Θ Ξ Escape Code */ + TWO(0xce, 0xa3), TWO(0xce, 0x98), TWO(0xce, 0x9e), ONE(0xa0), + /* Æ æ ß É */ + TWO(0xc3, 0x86), TWO(0xc3, 0xa6), TWO(0xc3, 0x9f), TWO(0xc3, 0x89), + /* ' ' ! " # */ + ONE(0x20), ONE(0x21), ONE(0x22), ONE(0x23), + /* ¤ % & ' */ + TWO(0xc2, 0xa4), ONE(0x25), ONE(0x26), ONE(0x27), + /* ( ) * + */ + ONE(0x28), ONE(0x29), ONE(0x2a), ONE(0x2b), + /* , - . / */ + ONE(0x2c), ONE(0x2d), ONE(0x2e), ONE(0x2f), + /* 0 1 2 3 */ + ONE(0x30), ONE(0x31), ONE(0x32), ONE(0x33), + /* 4 5 6 7 */ + ONE(0x34), ONE(0x35), ONE(0x36), ONE(0x37), + /* 8 9 : ; */ + ONE(0x38), ONE(0x39), ONE(0x3a), ONE(0x3b), + /* < = > ? */ + ONE(0x3c), ONE(0x3d), ONE(0x3e), ONE(0x3f), + /* ¡ A B C */ + TWO(0xc2, 0xa1), ONE(0x41), ONE(0x42), ONE(0x43), + /* D E F G */ + ONE(0x44), ONE(0x45), ONE(0x46), ONE(0x47), + /* H I J K */ + ONE(0x48), ONE(0x49), ONE(0x4a), ONE(0x4b), + /* L M N O */ + ONE(0x4c), ONE(0x4d), ONE(0x4e), ONE(0x4f), + /* P Q R S */ + ONE(0x50), ONE(0x51), ONE(0x52), ONE(0x53), + /* T U V W */ + ONE(0x54), ONE(0x55), ONE(0x56), ONE(0x57), + /* X Y Z Ä */ + ONE(0x58), ONE(0x59), ONE(0x5a), TWO(0xc3, 0x84), + /* Ö Ñ Ü § */ + TWO(0xc3, 0x96), TWO(0xc3, 0x91), TWO(0xc3, 0x9c), TWO(0xc2, 0xa7), + /* ¿ a b c */ + TWO(0xc2, 0xbf), ONE(0x61), ONE(0x62), ONE(0x63), + /* d e f g */ + ONE(0x64), ONE(0x65), ONE(0x66), ONE(0x67), + /* h i j k */ + ONE(0x68), ONE(0x69), ONE(0x6a), ONE(0x6b), + /* l m n o */ + ONE(0x6c), ONE(0x6d), ONE(0x6e), ONE(0x6f), + /* p q r s */ + ONE(0x70), ONE(0x71), ONE(0x72), ONE(0x73), + /* t u v w */ + ONE(0x74), ONE(0x75), ONE(0x76), ONE(0x77), + /* x y z ä */ + ONE(0x78), ONE(0x79), ONE(0x7a), TWO(0xc3, 0xa4), + /* ö ñ ü à */ + TWO(0xc3, 0xb6), TWO(0xc3, 0xb1), TWO(0xc3, 0xbc), TWO(0xc3, 0xa0) +}; + +static guint8 +gsm_def_char_to_utf8 (const guint8 gsm, guint8 out_utf8[2]) +{ + g_return_val_if_fail (gsm < GSM_DEF_ALPHABET_SIZE, 0); + memcpy (&out_utf8[0], &gsm_def_utf8_alphabet[gsm].chars[0], gsm_def_utf8_alphabet[gsm].len); + return gsm_def_utf8_alphabet[gsm].len; +} + +static gboolean +utf8_to_gsm_def_char (const char *utf8, guint32 len, guint8 *out_gsm) +{ + int i; + + if (len > 0 && len < 4) { + for (i = 0; i < GSM_DEF_ALPHABET_SIZE; i++) { + if (gsm_def_utf8_alphabet[i].len == len) { + if (memcmp (&gsm_def_utf8_alphabet[i].chars[0], utf8, len) == 0) { + *out_gsm = i; + return TRUE; + } + } + } + } + return FALSE; +} + + +#define EONE(a, g) { {a, 0x00, 0x00}, 1, g } +#define ETHR(a, b, c, g) { {a, b, c}, 3, g } + +/** + * gsm_ext_utf8_alphabet: + * + * Mapping from GSM extended alphabet to UTF-8. + * + */ +static const GsmUtf8Mapping gsm_ext_utf8_alphabet[GSM_EXT_ALPHABET_SIZE] = { + /* form feed ^ { } */ + EONE(0x0c, 0x0a), EONE(0x5e, 0x14), EONE(0x7b, 0x28), EONE(0x7d, 0x29), + /* \ [ ~ ] */ + EONE(0x5c, 0x2f), EONE(0x5b, 0x3c), EONE(0x7e, 0x3d), EONE(0x5d, 0x3e), + /* | € */ + EONE(0x7c, 0x40), ETHR(0xe2, 0x82, 0xac, 0x65) +}; + +#define GSM_ESCAPE_CHAR 0x1b + +static guint8 +gsm_ext_char_to_utf8 (const guint8 gsm, guint8 out_utf8[3]) +{ + int i; + + for (i = 0; i < GSM_EXT_ALPHABET_SIZE; i++) { + if (gsm == gsm_ext_utf8_alphabet[i].gsm) { + memcpy (&out_utf8[0], &gsm_ext_utf8_alphabet[i].chars[0], gsm_ext_utf8_alphabet[i].len); + return gsm_ext_utf8_alphabet[i].len; + } + } + return 0; +} + +static gboolean +utf8_to_gsm_ext_char (const char *utf8, guint32 len, guint8 *out_gsm) +{ + int i; + + if (len > 0 && len < 4) { + for (i = 0; i < GSM_EXT_ALPHABET_SIZE; i++) { + if (gsm_ext_utf8_alphabet[i].len == len) { + if (memcmp (&gsm_ext_utf8_alphabet[i].chars[0], utf8, len) == 0) { + *out_gsm = gsm_ext_utf8_alphabet[i].gsm; + return TRUE; + } + } + } + } + return FALSE; +} + +guint8 * +mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len) +{ + int i; + GByteArray *utf8; + + g_return_val_if_fail (gsm != NULL, NULL); + g_return_val_if_fail (len < 4096, NULL); + + /* worst case initial length */ + utf8 = g_byte_array_sized_new (len * 2 + 1); + + for (i = 0; i < len; i++) { + guint8 uchars[4]; + guint8 ulen; + + if (gsm[i] == GSM_ESCAPE_CHAR) { + /* Extended alphabet, decode next char */ + ulen = gsm_ext_char_to_utf8 (gsm[i+1], uchars); + if (ulen) + i += 1; + } else { + /* Default alphabet */ + ulen = gsm_def_char_to_utf8 (gsm[i], uchars); + } + + if (ulen) + g_byte_array_append (utf8, &uchars[0], ulen); + else + g_byte_array_append (utf8, (guint8 *) "?", 1); + } + + g_byte_array_append (utf8, (guint8 *) "\0", 1); /* NULL terminator */ + return g_byte_array_free (utf8, FALSE); +} + +guint8 * +mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len) +{ + GByteArray *gsm; + const char *c = utf8, *next = c; + static const guint8 gesc = GSM_ESCAPE_CHAR; + int i = 0; + + g_return_val_if_fail (utf8 != NULL, NULL); + g_return_val_if_fail (out_len != NULL, NULL); + g_return_val_if_fail (g_utf8_validate (utf8, -1, NULL), NULL); + + /* worst case initial length */ + gsm = g_byte_array_sized_new (g_utf8_strlen (utf8, -1) * 2 + 1); + + if (*utf8 == 0x00) { + /* Zero-length string */ + g_byte_array_append (gsm, (guint8 *) "\0", 1); + *out_len = 0; + return g_byte_array_free (gsm, FALSE); + } + + while (next && *next) { + guint8 gch = 0x3f; /* 0x3f == '?' */ + + next = g_utf8_next_char (c); + + /* Try escaped chars first, then default alphabet */ + if (utf8_to_gsm_ext_char (c, next - c, &gch)) { + /* Add the escape char */ + g_byte_array_append (gsm, &gesc, 1); + g_byte_array_append (gsm, &gch, 1); + } else if (utf8_to_gsm_def_char (c, next - c, &gch)) + g_byte_array_append (gsm, &gch, 1); + + c = next; + i++; + } + + *out_len = gsm->len; + return g_byte_array_free (gsm, FALSE); +} + +guint8 * +gsm_unpack (const guint8 *gsm, + guint32 gsm_len, + guint8 start_offset, /* in _bits_ */ + guint32 *out_unpacked_len) +{ + GByteArray *unpacked; + int i, nchars; + + nchars = ((gsm_len * 8) - start_offset) / 7; + unpacked = g_byte_array_sized_new (nchars + 1); + + for (i = 0; i < nchars; i++) { + guint8 bits_here, bits_in_next, octet, offset, c; + guint32 start_bit; + + start_bit = start_offset + (i * 7); /* Overall bit offset of char in buffer */ + offset = start_bit % 8; /* Offset to start of char in this byte */ + bits_here = offset ? (8 - offset) : 7; + bits_in_next = 7 - bits_here; + + /* Grab bits in the current byte */ + octet = gsm[start_bit / 8]; + c = (octet >> offset) & (0xFF >> (8 - bits_here)); + + /* Grab any bits that spilled over to next byte */ + if (bits_in_next) { + octet = gsm[(start_bit / 8) + 1]; + c |= (octet & (0xFF >> (8 - bits_in_next))) << bits_here; + } + g_byte_array_append (unpacked, &c, 1); + } + + *out_unpacked_len = unpacked->len; + return g_byte_array_free (unpacked, FALSE); +} + +guint8 * +gsm_pack (const guint8 *src, + guint32 src_len, + guint8 start_offset, + guint32 *out_packed_len) +{ + GByteArray *packed; + guint8 c, add_last = 0; + int i; + + packed = g_byte_array_sized_new (src_len); + + for (i = 0, c = 0; i < src_len; i++) { + guint8 bits_here, offset; + guint32 start_bit; + + start_bit = start_offset + (i * 7); /* Overall bit offset of char in buffer */ + offset = start_bit % 8; /* Offset to start of char in this byte */ + bits_here = offset ? (8 - offset) : 7; + + c |= (src[i] & 0x7F) << offset; + if (offset) { + /* Add this packed byte */ + g_byte_array_append (packed, &c, 1); + c = add_last = 0; + } + + /* Pack the rest of this char into the next byte */ + if (bits_here != 7) { + c = (src[i] & 0x7F) >> bits_here; + add_last = 1; + } + } + if (add_last) + g_byte_array_append (packed, &c, 1); + + *out_packed_len = packed->len; + return g_byte_array_free (packed, FALSE); } diff --git a/src/mm-charsets.h b/src/mm-charsets.h index 5fa3406..661052d 100644 --- a/src/mm-charsets.h +++ b/src/mm-charsets.h @@ -39,7 +39,7 @@ MMModemCharset mm_modem_charset_from_string (const char *string); * UTF-8 encoded. */ gboolean mm_modem_charset_byte_array_append (GByteArray *array, - const char *string, + const char *utf8, gboolean quoted, MMModemCharset charset); @@ -48,5 +48,19 @@ gboolean mm_modem_charset_byte_array_append (GByteArray *array, */ char *mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset); +guint8 *mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len); + +guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len); + +guint8 *gsm_unpack (const guint8 *gsm, + guint32 gsm_len, + guint8 start_offset, /* in bits */ + guint32 *out_unpacked_len); + +guint8 *gsm_pack (const guint8 *src, + guint32 src_len, + guint8 start_offset, /* in bits */ + guint32 *out_packed_len); + #endif /* MM_CHARSETS_H */ diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 9fe897f..0a95e7b 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -30,6 +30,7 @@ #include "mm-serial-parsers.h" #include "mm-modem-helpers.h" #include "libqcdm/src/commands.h" +#include "mm-log.h" #define MM_GENERIC_CDMA_PREV_STATE_TAG "prev-state" @@ -68,6 +69,8 @@ typedef struct { guint poll_id; + char *meid; + MMModemCdmaRegistrationState cdma_1x_reg_state; MMModemCdmaRegistrationState evdo_reg_state; @@ -95,7 +98,9 @@ mm_generic_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA) + gboolean evdo_revA, + guint vendor, + guint product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -107,6 +112,8 @@ mm_generic_cdma_new (const char *device, MM_MODEM_PLUGIN, plugin, MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -162,6 +169,47 @@ initial_esn_check (MMGenericCdma *self) } } +static void +get_info_cb (MMModem *modem, + const char *manufacturer, + const char *model, + const char *version, + GError *error, + gpointer user_data) +{ + /* Base class handles saving the info for us */ + if (modem) + mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_CDMA_GET_PRIVATE (modem)->primary)); +} + +static void +initial_info_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_base_get_card_info (MM_MODEM_BASE (self), + priv->primary, + NULL, + get_info_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) { @@ -215,6 +263,9 @@ mm_generic_cdma_grab_port (MMGenericCdma *self, g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); } + /* Get the modem's general info */ + initial_info_check (self); + /* Get modem's ESN number */ initial_esn_check (self); @@ -315,6 +366,15 @@ mm_generic_cdma_get_best_at_port (MMGenericCdma *self, GError **error) return priv->secondary; } +MMQcdmSerialPort * +mm_generic_cdma_get_best_qcdm_port (MMGenericCdma *self, GError **error) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (MM_IS_GENERIC_CDMA (self), NULL); + + return MM_GENERIC_CDMA_GET_PRIVATE (self)->qcdm; +} + /*****************************************************************************/ void @@ -551,25 +611,6 @@ out: mm_callback_info_schedule (info); } -static void -enable_error_reporting_done (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); - - /* Just ignore errors, see comment in init_done() */ - if (error) - g_warning ("Your CDMA modem does not support +CMEE command"); - - if (MM_GENERIC_CDMA_GET_CLASS (self)->post_enable) - MM_GENERIC_CDMA_GET_CLASS (self)->post_enable (self, enable_all_done, info); - else - enable_all_done (MM_MODEM (self), NULL, info); -} - static void init_done (MMAtSerialPort *port, GString *response, @@ -586,12 +627,17 @@ init_done (MMAtSerialPort *port, info->error = g_error_copy (error); mm_callback_info_schedule (info); } else { - /* Try to enable better error reporting. My experience so far indicates - there's some CDMA modems that does not support that. - 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_at_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data); + MMGenericCdma *self = MM_GENERIC_CDMA (info->modem); + + /* Try enabling better error reporting on CDMA devices, but few + * actually support +CMEE as it's more of a GSM command. + */ + mm_at_serial_port_queue_command (port, "+CMEE=1", 3, NULL, NULL); + + if (MM_GENERIC_CDMA_GET_CLASS (self)->post_enable) + MM_GENERIC_CDMA_GET_CLASS (self)->post_enable (self, enable_all_done, info); + else + enable_all_done (MM_MODEM (self), NULL, info); } } @@ -1002,7 +1048,7 @@ get_signal_quality (MMModemCdma *modem, 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_dbg ("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; @@ -1481,15 +1527,22 @@ reg_query_speri_done (MMAtSerialPort *port, 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) { + /* Change the 1x and EVDO registration states to roaming if they were + * anything other than UNKNOWN. + */ 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); + } else { + /* Change 1x and/or EVDO registration state to home if home/roaming wasn't previously known */ + if (mm_generic_cdma_query_reg_state_get_callback_1x_state (info) == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_HOME); + + if (mm_generic_cdma_query_reg_state_get_callback_evdo_state (info) == MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED) + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_HOME); } done: @@ -1541,6 +1594,12 @@ real_query_registration_state (MMGenericCdma *self, port = mm_generic_cdma_get_best_at_port (self, &info->error); if (!port) { + /* If we can't get an AT port, but less specific registration checks + * were successful, just use that and don't return an error. + */ + if ( cur_cdma_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN + || cur_evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) + g_clear_error (&info->error); mm_callback_info_schedule (info); return; } @@ -1658,58 +1717,79 @@ error: } static void -reg_cmstate_cb (MMQcdmSerialPort *port, - GByteArray *response, - GError *error, - gpointer user_data) +reg_hdrstate_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = user_data; - MMAtSerialPort *at_port; - QCDMResult *result; - guint32 opmode = 0, sysmode = 0; + QCDMResult *result = NULL; + guint32 sysmode; MMModemCdmaRegistrationState cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + MMAtSerialPort *at_port; + gboolean evdo_registered = FALSE; 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; + sysmode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "sysmode")); - 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); + /* Get HDR subsystem state to determine EVDO registration when in 1X mode */ + result = qcdm_cmd_hdr_subsys_state_info_result ((const char *) response->data, + response->len, + NULL); + if (result) { + guint8 session_state = QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_CLOSED; + guint8 almp_state = QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_INACTIVE; + guint8 hybrid_mode = 0; - 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 ( qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_SESSION_STATE, &session_state) + && qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, &almp_state) + && qcdm_result_get_uint8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_HDR_HYBRID_MODE, &hybrid_mode)) { - 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. + /* EVDO state is registered if the HDR subsystem is registered, and + * we're in hybrid mode, and the Call Manager system mode is + * CDMA. */ - 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; - } + if ( hybrid_mode + && session_state == QCDM_CMD_HDR_SUBSYS_STATE_INFO_SESSION_STATE_OPEN + && ( almp_state == QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_IDLE + || almp_state == QCDM_CMD_HDR_SUBSYS_STATE_INFO_ALMP_STATE_CONNECTED)) + evdo_registered = TRUE; + } + + qcdm_result_unref (result); + } + + switch (sysmode) { + case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_CDMA: + cdma_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + if (evdo_registered) + evdo_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; } } @@ -1727,6 +1807,59 @@ error: mm_callback_info_schedule (info); } +static void +reg_cmstate_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMAtSerialPort *at_port = NULL; + QCDMResult *result = NULL; + guint32 opmode = 0, sysmode = 0; + GError *qcdm_error = NULL; + + /* Parse the response */ + if (!error) + result = qcdm_cmd_cm_subsys_state_info_result ((const char *) response->data, response->len, &qcdm_error); + + if (!result) { + /* If there was some error, fall back to use +CAD like we did before QCDM */ + if (info->modem) + at_port = mm_generic_cdma_get_best_at_port (MM_GENERIC_CDMA (info->modem), &info->error); + else + info->error = g_error_copy (qcdm_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); + g_clear_error (&qcdm_error); + return; + } + + 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) { + GByteArray *hdrstate; + + mm_callback_info_set_data (info, "sysmode", GUINT_TO_POINTER (sysmode), NULL); + + /* Get HDR subsystem state */ + hdrstate = g_byte_array_sized_new (25); + hdrstate->len = qcdm_cmd_hdr_subsys_state_info_new ((char *) hdrstate->data, 25, NULL); + g_assert (hdrstate->len); + mm_qcdm_serial_port_queue_command (port, hdrstate, 3, reg_hdrstate_cb, info); + } else { + /* No service */ + set_callback_1x_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + set_callback_evdo_state_helper (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + mm_callback_info_schedule (info); + } +} + static void get_registration_state (MMModemCdma *modem, MMModemCdmaRegistrationStateFn callback, @@ -1744,13 +1877,14 @@ get_registration_state (MMModemCdma *modem, 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_dbg ("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; } + g_clear_error (&info->error); /* 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 @@ -2155,6 +2289,9 @@ get_property (GObject *object, guint prop_id, case MM_MODEM_PROP_TYPE: g_value_set_uint (value, MM_MODEM_TYPE_CDMA); break; + case MM_MODEM_CDMA_PROP_MEID: + g_value_set_string (value, priv->meid); + break; case PROP_EVDO_REV0: g_value_set_boolean (value, priv->evdo_rev0); break; @@ -2207,6 +2344,10 @@ mm_generic_cdma_class_init (MMGenericCdmaClass *klass) MM_MODEM_PROP_TYPE, MM_MODEM_TYPE); + g_object_class_override_property (object_class, + MM_MODEM_CDMA_PROP_MEID, + MM_MODEM_CDMA_MEID); + g_object_class_install_property (object_class, PROP_EVDO_REV0, g_param_spec_boolean (MM_GENERIC_CDMA_EVDO_REV0, "EVDO rev0", diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h index e4f9e57..350c58e 100644 --- a/src/mm-generic-cdma.h +++ b/src/mm-generic-cdma.h @@ -21,6 +21,7 @@ #include "mm-modem-base.h" #include "mm-modem-cdma.h" #include "mm-at-serial-port.h" +#include "mm-qcdm-serial-port.h" #include "mm-callback-info.h" #define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ()) @@ -87,7 +88,9 @@ MMModem *mm_generic_cdma_new (const char *device, const char *driver, const char *plugin, gboolean evdo_rev0, - gboolean evdo_revA); + gboolean evdo_revA, + guint vendor, + guint product); /* Private, for subclasses */ @@ -103,6 +106,9 @@ MMAtSerialPort *mm_generic_cdma_get_at_port (MMGenericCdma *modem, MMPortType pt MMAtSerialPort *mm_generic_cdma_get_best_at_port (MMGenericCdma *modem, GError **error); +MMQcdmSerialPort *mm_generic_cdma_get_best_qcdm_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); diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 08cde10..98713b0 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -25,6 +25,7 @@ #include "mm-modem-gsm-card.h" #include "mm-modem-gsm-network.h" #include "mm-modem-gsm-sms.h" +#include "mm-modem-gsm-ussd.h" #include "mm-modem-simple.h" #include "mm-errors.h" #include "mm-callback-info.h" @@ -32,21 +33,26 @@ #include "mm-qcdm-serial-port.h" #include "mm-serial-parsers.h" #include "mm-modem-helpers.h" -#include "mm-options.h" +#include "mm-log.h" #include "mm-properties-changed-signal.h" #include "mm-utils.h" +#include "mm-modem-location.h" static void modem_init (MMModem *modem_class); static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); static void modem_gsm_sms_init (MMModemGsmSms *gsm_sms_class); +static void modem_gsm_ussd_init (MMModemGsmUssd *gsm_ussd_class); static void modem_simple_init (MMModemSimple *class); +static void modem_location_init (MMModemLocation *class); G_DEFINE_TYPE_EXTENDED (MMGenericGsm, mm_generic_gsm, MM_TYPE_MODEM_BASE, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init) G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_SMS, modem_gsm_sms_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_LOCATION, modem_location_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_USSD, modem_gsm_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) #define MM_GENERIC_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_GSM, MMGenericGsmPrivate)) @@ -60,6 +66,9 @@ typedef struct { gboolean pin_checked; guint32 pin_check_tries; guint pin_check_timeout; + char *simid; + gboolean simid_checked; + guint32 simid_tries; MMModemGsmAllowedMode allowed_mode; @@ -87,8 +96,14 @@ typedef struct { MMCallbackInfo *pending_reg_info; gboolean manual_reg; + gboolean cmer_enabled; + guint roam_ind; + guint signal_ind; + guint service_ind; + guint signal_quality_id; - time_t signal_quality_timestamp; + time_t signal_emit_timestamp; + time_t signal_update_timestamp; guint32 signal_quality; gint cid; @@ -99,6 +114,13 @@ typedef struct { MMAtSerialPort *secondary; MMQcdmSerialPort *qcdm; MMPort *data; + + /* Location API */ + guint32 loc_caps; + gboolean loc_enabled; + gboolean loc_signal; + + MMModemGsmUssdState ussd_state; } MMGenericGsmPrivate; static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info); @@ -139,10 +161,18 @@ static void reg_info_updated (MMGenericGsm *self, gboolean update_name, const char *oper_name); +static void update_lac_ci (MMGenericGsm *self, gulong lac, gulong ci, guint idx); + +static void ciev_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data); + MMModem * mm_generic_gsm_new (const char *device, const char *driver, - const char *plugin) + const char *plugin, + guint vendor, + guint product) { g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (driver != NULL, NULL); @@ -152,6 +182,8 @@ mm_generic_gsm_new (const char *device, MM_MODEM_MASTER_DEVICE, device, MM_MODEM_DRIVER, driver, MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, NULL)); } @@ -235,6 +267,10 @@ pin_check_done (MMAtSerialPort *port, else if (response && strstr (response->str, "+CPIN: ")) { const char *str = strstr (response->str, "+CPIN: ") + 7; + /* Some phones (Motorola EZX models) seem to quote the response */ + if (str[0] == '"') + str++; + 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) @@ -306,12 +342,26 @@ get_imei_cb (MMModem *modem, } } +static void +get_info_cb (MMModem *modem, + const char *manufacturer, + const char *model, + const char *version, + GError *error, + gpointer user_data) +{ + /* Base class handles saving the info for us */ + if (modem) + mm_serial_port_close (MM_SERIAL_PORT (MM_GENERIC_GSM_GET_PRIVATE (modem)->primary)); +} + /*****************************************************************************/ static MMModemGsmNetworkRegStatus -gsm_reg_status (MMGenericGsm *self) +gsm_reg_status (MMGenericGsm *self, guint32 *out_idx) { MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + guint32 idx = 1; /* Some devices (Blackberries for example) will respond to +CGREG, but * return ERROR for +CREG, probably because their firmware is just stupid. @@ -320,23 +370,36 @@ gsm_reg_status (MMGenericGsm *self) */ 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]; + || priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) { + idx = 0; + goto out; + } 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]; + || priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) { + idx = 1; + goto out; + } - if (priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING) - return priv->reg_status[0]; + if (priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING) { + idx = 0; + goto out; + } - if (priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING) - return priv->reg_status[1]; + if (priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING) { + idx = 1; + goto out; + } - if (priv->reg_status[0] != MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN) - return priv->reg_status[0]; + if (priv->reg_status[0] != MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN) { + idx = 0; + goto out; + } - return priv->reg_status[1]; +out: + if (out_idx) + *out_idx = idx; + return priv->reg_status[idx]; } void @@ -350,7 +413,7 @@ mm_generic_gsm_update_enabled_state (MMGenericGsm *self, if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING)) return; - switch (gsm_reg_status (self)) { + switch (gsm_reg_status (self, NULL)) { 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); @@ -373,13 +436,211 @@ check_valid (MMGenericGsm *self) MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); gboolean new_valid = FALSE; - if (priv->primary && priv->data && priv->pin_checked) + if (priv->primary && priv->data && priv->pin_checked && priv->simid_checked) new_valid = TRUE; mm_modem_base_set_valid (MM_MODEM_BASE (self), new_valid); } +static void +get_iccid_done (MMModem *modem, + const char *response, + GError *error, + gpointer user_data) +{ + MMGenericGsmPrivate *priv; + const char *p = response; + GChecksum *sum = NULL; + + if (error || !response || !strlen (response)) + goto done; + + sum = g_checksum_new (G_CHECKSUM_SHA1); + + /* Make sure it looks like an ICCID */ + while (*p) { + if (!isdigit (*p)) { + g_warning ("%s: invalid ICCID format (not a digit)", __func__); + goto done; + } + g_checksum_update (sum, (const guchar *) p++, 1); + } + + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + g_free (priv->simid); + priv->simid = g_strdup (g_checksum_get_string (sum)); + + mm_dbg ("SIM ID source '%s'", response); + mm_dbg ("SIM ID '%s'", priv->simid); + + g_object_notify (G_OBJECT (modem), MM_MODEM_GSM_CARD_SIM_IDENTIFIER); + +done: + if (sum) + g_checksum_free (sum); + + if (modem) { + MM_GENERIC_GSM_GET_PRIVATE (modem)->simid_checked = TRUE; + check_valid (MM_GENERIC_GSM (modem)); + } +} + +#define ICCID_CMD "+CRSM=176,12258,0,0,10" + +static void +real_get_iccid_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *str; + int sw1, sw2; + gboolean success = FALSE; + char buf[21], swapped[21]; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + memset (buf, 0, sizeof (buf)); + str = mm_strip_tag (response->str, "+CRSM:"); + if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3) + success = TRUE; + else { + /* May not include quotes... */ + if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 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 len = 0; + int f_pos = -1, i; + + /* Make sure the buffer is only digits or 'F' */ + for (len = 0; len < sizeof (buf) && buf[len]; len++) { + if (isdigit (buf[len])) + continue; + if (buf[len] == 'F' || buf[len] == 'f') { + buf[len] = 'F'; /* canonicalize the F */ + f_pos = len; + continue; + } + if (buf[len] == '\"') { + buf[len] = 0; + break; + } + + /* Invalid character */ + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "CRSM ICCID response contained invalid character '%c'", + buf[len]); + goto done; + } + + /* BCD encoded ICCIDs are 20 digits long */ + if (len != 20) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Invalid +CRSM ICCID response size (was %zd, expected 20)", + len); + goto done; + } + + /* Ensure if there's an 'F' that it's second-to-last */ + if ((f_pos >= 0) && (f_pos != len - 2)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Invalid +CRSM ICCID length (unexpected F)"); + goto done; + } + + /* Swap digits in the EFiccid response to get the actual ICCID, each + * group of 2 digits is reversed in the +CRSM response. i.e.: + * + * 21436587 -> 12345678 + */ + memset (swapped, 0, sizeof (swapped)); + for (i = 0; i < 10; i++) { + swapped[i * 2] = buf[(i * 2) + 1]; + swapped[(i * 2) + 1] = buf[i * 2]; + } + + /* Zero out the F for 19 digit ICCIDs */ + if (swapped[len - 1] == 'F') + swapped[len - 1] = 0; + + mm_callback_info_set_result (info, g_strdup (swapped), g_free); + } else { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + if (priv->simid_tries++ < 2) { + /* Try one more time... Gobi 1K cards may reply to the first + * request with '+CRSM: 106,134,""' which is bogus because + * subsequent requests work fine. + */ + mm_at_serial_port_queue_command (port, ICCID_CMD, 20, real_get_iccid_done, info); + return; + } 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: + /* Balance open from real_get_sim_iccid() */ + mm_serial_port_close (MM_SERIAL_PORT (port)); + + mm_callback_info_schedule (info); +} + +static void +real_get_sim_iccid (MMGenericGsm *self, + MMModemStringFn callback, + gpointer callback_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + GError *error = NULL; + + port = mm_generic_gsm_get_best_at_port (self, &error); + if (!port) { + callback (MM_MODEM (self), NULL, error, callback_data); + g_clear_error (&error); + return; + } + + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &error)) { + callback (MM_MODEM (self), NULL, error, callback_data); + g_clear_error (&error); + return; + } + + info = mm_callback_info_string_new (MM_MODEM (self), callback, callback_data); + + /* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */ + mm_at_serial_port_queue_command (port, ICCID_CMD, 20, real_get_iccid_done, info); +} + +static void +initial_iccid_check (MMGenericGsm *self) +{ + g_assert (MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid); + MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid (self, get_iccid_done, NULL); +} + static void initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data); static gboolean @@ -415,9 +676,13 @@ initial_pin_check_done (MMModem *modem, GError *error, gpointer user_data) g_source_remove (priv->pin_check_timeout); priv->pin_check_timeout = g_timeout_add_seconds (2, pin_check_again, modem); } else { + /* Try to get the SIM ICCID after we've checked PIN status and the SIM + * is ready. + */ + initial_iccid_check (MM_GENERIC_GSM (modem)); + priv->pin_checked = TRUE; mm_serial_port_close (MM_SERIAL_PORT (priv->primary)); - check_valid (MM_GENERIC_GSM (modem)); } } @@ -476,6 +741,34 @@ initial_imei_check (MMGenericGsm *self) } } +static void +initial_info_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); + mm_modem_base_get_card_info (MM_MODEM_BASE (self), + priv->primary, + NULL, + get_info_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) { @@ -519,6 +812,10 @@ mm_generic_gsm_grab_port (MMGenericGsm *self, } mm_gsm_creg_regex_destroy (array); + regex = g_regex_new ("\\r\\n\\+CIEV: (\\d+),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, ciev_received, self, NULL); + g_regex_unref (regex); + if (ptype == MM_PORT_TYPE_PRIMARY) { priv->primary = MM_AT_SERIAL_PORT (port); if (!priv->data) { @@ -526,12 +823,17 @@ mm_generic_gsm_grab_port (MMGenericGsm *self, g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE); } - /* Get modem's initial lock/unlock state */ - initial_pin_check (self); + /* Get the modem's general info */ + initial_info_check (self); - /* Get modem's IMEI number */ + /* Get modem's IMEI */ initial_imei_check (self); + /* Get modem's initial lock/unlock state; this also ensures the + * SIM is ready by waiting if necessary for the SIM to initalize. + */ + initial_pin_check (self); + } else if (ptype == MM_PORT_TYPE_SECONDARY) priv->secondary = MM_AT_SERIAL_PORT (port); } else if (MM_IS_QCDM_SERIAL_PORT (port)) { @@ -614,6 +916,18 @@ release_port (MMModem *modem, const char *subsys, const char *name) check_valid (MM_GENERIC_GSM (modem)); } +static void +add_loc_capability (MMGenericGsm *self, guint32 cap) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + guint32 old_caps = priv->loc_caps; + + priv->loc_caps |= cap; + if (priv->loc_caps != old_caps) { + g_object_notify (G_OBJECT (self), MM_MODEM_LOCATION_CAPABILITIES); + } +} + static void reg_poll_response (MMAtSerialPort *port, GString *response, @@ -661,9 +975,12 @@ periodic_poll_cb (gpointer user_data) 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); + /* Don't poll signal quality if we got a notification in the past 10 seconds */ + if (time (NULL) - priv->signal_update_timestamp > 10) { + 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); @@ -671,6 +988,57 @@ periodic_poll_cb (gpointer user_data) return TRUE; /* continue running */ } +#define CREG_NUM_TAG "creg-num" +#define CGREG_NUM_TAG "cgreg-num" + +static void +initial_unsolicited_reg_check_done (MMCallbackInfo *info) +{ + MMGenericGsmPrivate *priv; + guint creg_num, cgreg_num; + + if (!info->modem || info->error) + goto done; + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + if (!priv->secondary) + goto done; + + /* Enable unsolicited registration responses on secondary ports too, + * to ensure that we get the response even if the modem is connected + * on the primary port. We enable responses on both ports because we + * cannot trust modems to reliably send the responses on the port we + * enable them on. + */ + + creg_num = GPOINTER_TO_UINT (mm_callback_info_get_data (info, CREG_NUM_TAG)); + switch (creg_num) { + case 1: + mm_at_serial_port_queue_command (priv->secondary, "+CREG=1", 3, NULL, NULL); + break; + case 2: + mm_at_serial_port_queue_command (priv->secondary, "+CREG=2", 3, NULL, NULL); + break; + default: + break; + } + + cgreg_num = GPOINTER_TO_UINT (mm_callback_info_get_data (info, CGREG_NUM_TAG)); + switch (cgreg_num) { + case 1: + mm_at_serial_port_queue_command (priv->secondary, "+CGREG=1", 3, NULL, NULL); + break; + case 2: + mm_at_serial_port_queue_command (priv->secondary, "+CGREG=2", 3, NULL, NULL); + break; + default: + break; + } + +done: + mm_callback_info_schedule (info); +} + static void cgreg1_done (MMAtSerialPort *port, GString *response, @@ -688,11 +1056,14 @@ cgreg1_done (MMAtSerialPort *port, /* The modem doesn't like unsolicited CGREG, so we'll need to poll */ priv->cgreg_poll = TRUE; - } + } else + mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (1), NULL); + /* Success; get initial state */ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem); } - mm_callback_info_schedule (info); + + initial_unsolicited_reg_check_done (info); } static void @@ -711,11 +1082,15 @@ cgreg2_done (MMAtSerialPort *port, /* Try CGREG=1 instead */ mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info); } else { + add_loc_capability (MM_GENERIC_GSM (info->modem), MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI); + + mm_callback_info_set_data (info, CGREG_NUM_TAG, GUINT_TO_POINTER (2), NULL); + /* 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); + initial_unsolicited_reg_check_done (info); } } else { /* Modem got removed */ @@ -740,7 +1115,9 @@ creg1_done (MMAtSerialPort *port, /* The modem doesn't like unsolicited CREG, so we'll need to poll */ priv->creg_poll = TRUE; - } + } else + mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (1), NULL); + /* Success; get initial state */ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem); @@ -767,6 +1144,10 @@ creg2_done (MMAtSerialPort *port, g_clear_error (&info->error); mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info); } else { + add_loc_capability (MM_GENERIC_GSM (info->modem), MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI); + + mm_callback_info_set_data (info, CREG_NUM_TAG, GUINT_TO_POINTER (2), NULL); + /* Success; get initial state */ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem); @@ -807,6 +1188,7 @@ static guint32 best_charsets[] = { MM_MODEM_CHARSET_UCS2, MM_MODEM_CHARSET_8859_1, MM_MODEM_CHARSET_IRA, + MM_MODEM_CHARSET_GSM, MM_MODEM_CHARSET_UNKNOWN }; @@ -865,7 +1247,7 @@ supported_charsets_done (MMModem *modem, } /* 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); + mm_modem_set_charset (modem, best_charsets[0], enabled_set_charset_done, info); } static void @@ -881,14 +1263,87 @@ get_allowed_mode_done (MMModem *modem, } static void -get_enable_info_done (MMModem *modem, - const char *manufacturer, - const char *model, - const char *version, - GError *error, - gpointer user_data) +ciev_received (MMAtSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (user_data); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + int quality = 0, ind = 0; + char *str; + + if (!priv->cmer_enabled) + return; + + str = g_match_info_fetch (info, 1); + if (str) + ind = atoi (str); + g_free (str); + + if (ind == priv->signal_ind) { + str = g_match_info_fetch (info, 2); + if (str) { + quality = atoi (str); + mm_generic_gsm_update_signal_quality (self, quality * 20); + } + g_free (str); + } + + /* FIXME: handle roaming and service indicators */ +} + +static void +cmer_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + if (!error) { + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data); + + priv->cmer_enabled = TRUE; + + /* Enable CMER on the secondary port if we can too */ + if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) + mm_at_serial_port_queue_command (priv->secondary, "+CMER=3,0,0,1", 3, NULL, NULL); + } +} + +static void +cind_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { - /* Modem base class handles the response for us */ + MMGenericGsm *self; + MMGenericGsmPrivate *priv; + GHashTable *indicators; + + if (error) + return; + + self = MM_GENERIC_GSM (user_data); + priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + indicators = mm_parse_cind_test_response (response->str, NULL); + if (indicators) { + CindResponse *r; + + r = g_hash_table_lookup (indicators, "signal"); + if (r) + priv->signal_ind = cind_response_get_index (r); + + r = g_hash_table_lookup (indicators, "roam"); + if (r) + priv->roam_ind = cind_response_get_index (r); + + r = g_hash_table_lookup (indicators, "service"); + if (r) + priv->service_ind = cind_response_get_index (r); + + mm_at_serial_port_queue_command (port, "+CMER=3,0,0,1", 3, cmer_cb, self); + g_hash_table_destroy (indicators); + } } void @@ -916,20 +1371,20 @@ mm_generic_gsm_enable_complete (MMGenericGsm *self, */ 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_dbg ("error opening secondary port: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); } } /* 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); + mm_at_serial_port_queue_command (priv->primary, "+CIND=?", 3, cind_cb, self); + + /* Try one more time to get the SIM ID */ + if (!priv->simid) + MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid (self, get_iccid_done, NULL); /* Get allowed mode */ if (MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode) @@ -1109,6 +1564,7 @@ disable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) { + MMGenericGsmPrivate *priv; MMCallbackInfo *info = user_data; MMModemState prev_state; char *cmd = NULL; @@ -1127,9 +1583,21 @@ disable_flash_done (MMSerialPort *port, return; } + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + /* 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); + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CREG=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CGREG=0", 3, NULL, NULL); + + if (priv->cmer_enabled) { + mm_at_serial_port_queue_command (MM_AT_SERIAL_PORT (port), "+CMER=0", 3, NULL, NULL); + + /* And on the secondary port */ + if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) + mm_at_serial_port_queue_command (priv->secondary, "+CMER=0", 3, NULL, NULL); + + priv->cmer_enabled = FALSE; + } g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_DOWN_CMD, &cmd, NULL); if (cmd && strlen (cmd)) @@ -1139,6 +1607,15 @@ disable_flash_done (MMSerialPort *port, g_free (cmd); } +static void +secondary_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_serial_port_close_force (MM_SERIAL_PORT (port)); +} + static void disable (MMModem *modem, MMModemFn callback, @@ -1170,15 +1647,16 @@ disable (MMModem *modem, priv->pin_check_timeout = 0; } - priv->lac[0] = 0; - priv->lac[1] = 0; - priv->cell_id[0] = 0; - priv->cell_id[1] = 0; + update_lac_ci (self, 0, 0, 0); + update_lac_ci (self, 0, 0, 1); _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)); + /* Clean up the secondary port if it's open */ + if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary))) { + mm_at_serial_port_queue_command (priv->secondary, "+CREG=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (priv->secondary, "+CGREG=0", 3, NULL, NULL); + mm_at_serial_port_queue_command (priv->secondary, "+CMER=0", 3, secondary_unsolicited_done, NULL); + } info = mm_callback_info_new (modem, callback, user_data); @@ -1370,6 +1848,7 @@ get_card_info (MMModem *modem, } #define PIN_PORT_TAG "pin-port" +#define SAVED_ERROR_TAG "error" static void pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data); @@ -1389,6 +1868,7 @@ pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMSerialPort *port; + GError *saved_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 @@ -1429,6 +1909,13 @@ pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data) if (modem && port) mm_serial_port_close (port); + /* If we have a saved error from sending PIN/PUK, return that to callers */ + saved_error = mm_callback_info_get_data (info, SAVED_ERROR_TAG); + if (saved_error) { + g_clear_error (&info->error); + info->error = saved_error; + } + mm_callback_info_schedule (info); } @@ -1441,10 +1928,18 @@ send_puk_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - mm_serial_port_close (MM_SERIAL_PORT (port)); - return; + if (error->domain != MM_MOBILE_ERROR) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + mm_serial_port_close (MM_SERIAL_PORT (port)); + return; + } else { + /* Keep the real error around so we can send it back + * when we're done rechecking CPIN status. + */ + mm_callback_info_set_data (info, SAVED_ERROR_TAG, + g_error_copy (error), NULL); + } } /* Get latest PIN status */ @@ -1496,10 +1991,18 @@ send_pin_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - mm_serial_port_close (MM_SERIAL_PORT (port)); - return; + if (error->domain != MM_MOBILE_ERROR) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + mm_serial_port_close (MM_SERIAL_PORT (port)); + return; + } else { + /* Keep the real error around so we can send it back + * when we're done rechecking CPIN status. + */ + mm_callback_info_set_data (info, SAVED_ERROR_TAG, + g_error_copy (error), NULL); + } } /* Get latest PIN status */ @@ -1634,9 +2137,9 @@ reg_info_updated (MMGenericGsm *self, 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); + old_status = gsm_reg_status (self, NULL); priv->reg_status[rs_type - 1] = status; - if (gsm_reg_status (self) != old_status) + if (gsm_reg_status (self, NULL) != old_status) changed = TRUE; } @@ -1658,7 +2161,7 @@ reg_info_updated (MMGenericGsm *self, if (changed) { mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (self), - gsm_reg_status (self), + gsm_reg_status (self, NULL), priv->oper_code, priv->oper_name); } @@ -1716,12 +2219,22 @@ parse_operator (const char *reply, MMModemCharset cur_charset) 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); + if (operator) { + /* 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 (cur_charset == MM_MODEM_CHARSET_UCS2) + convert_operator_from_ucs2 (&operator); + + /* Ensure the operator name is valid UTF-8 so that we can send it + * through D-Bus and such. + */ + if (!g_utf8_validate (operator, -1, NULL)) { + g_free (operator); + operator = NULL; + } + } return operator; } @@ -1803,7 +2316,7 @@ roam_disconnect_done (MMModem *modem, GError *error, gpointer user_data) { - g_message ("Disconnected because roaming is not allowed"); + mm_info ("Disconnected because roaming is not allowed"); } static void @@ -1834,9 +2347,9 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *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); + mm_dbg ("%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); @@ -1948,18 +2461,15 @@ reg_state_changed (MMAtSerialPort *port, { MMGenericGsm *self = MM_GENERIC_GSM (user_data); MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); - guint32 state = 0, idx; + guint32 state = 0; gulong lac = 0, cell_id = 0; gint act = -1; gboolean cgreg = FALSE; GError *error = NULL; 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)"); - } + mm_warn ("error parsing unsolicited registration: %s", + error && error->message ? error->message : "(unknown)"); return; } @@ -1974,9 +2484,7 @@ reg_state_changed (MMAtSerialPort *port, } } - idx = cgreg ? 1 : 0; - priv->lac[idx] = lac; - priv->cell_id[idx] = cell_id; + update_lac_ci (self, lac, cell_id, cgreg ? 1 : 0); /* Only update access technology if it appeared in the CREG/CGREG response */ if (act != -1) @@ -2014,7 +2522,7 @@ handle_reg_status_response (MMGenericGsm *self, { MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); GMatchInfo *match_info; - guint32 status = 0, idx; + guint32 status = 0; gulong lac = 0, ci = 0; gint act = -1; gboolean cgreg = FALSE; @@ -2043,9 +2551,7 @@ handle_reg_status_response (MMGenericGsm *self, } /* Success; update cached location information */ - idx = cgreg ? 1 : 0; - priv->lac[idx] = lac; - priv->cell_id[idx] = ci; + update_lac_ci (self, lac, ci, cgreg ? 1 : 0); /* Only update access technology if it appeared in the CREG/CGREG response */ if (act != -1) @@ -2108,7 +2614,7 @@ get_reg_status_done (MMAtSerialPort *port, goto reg_done; } - status = gsm_reg_status (self); + status = gsm_reg_status (self, NULL); 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) { @@ -2222,7 +2728,7 @@ do_register (MMModemGsmNetwork *modem, if (network_id) { command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id); priv->manual_reg = TRUE; - } else if (reg_is_idle (gsm_reg_status (self)) || priv->manual_reg) { + } else if (reg_is_idle (gsm_reg_status (self, NULL)) || priv->manual_reg) { command = g_strdup ("+COPS=0,,"); priv->manual_reg = FALSE; } @@ -2260,7 +2766,7 @@ gsm_network_reg_info_invoke (MMCallbackInfo *info) MMModemGsmNetworkRegInfoFn callback = (MMModemGsmNetworkRegInfoFn) info->callback; callback (MM_MODEM_GSM_NETWORK (info->modem), - gsm_reg_status (MM_GENERIC_GSM (info->modem)), + gsm_reg_status (MM_GENERIC_GSM (info->modem), NULL), priv->oper_code, priv->oper_name, info->error, @@ -2369,7 +2875,7 @@ connect (MMModem *modem, MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); MMCallbackInfo *info; char *command; - guint32 cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)); + gint cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)); info = mm_callback_info_new (modem, callback, user_data); @@ -2803,7 +3309,7 @@ emit_signal_quality_change (gpointer user_data) MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); priv->signal_quality_id = 0; - priv->signal_quality_timestamp = time (NULL); + priv->signal_emit_timestamp = time (NULL); mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), priv->signal_quality); return FALSE; } @@ -2820,6 +3326,8 @@ mm_generic_gsm_update_signal_quality (MMGenericGsm *self, guint32 quality) priv = MM_GENERIC_GSM_GET_PRIVATE (self); + priv->signal_update_timestamp = time (NULL); + if (priv->signal_quality == quality) return; @@ -2831,12 +3339,12 @@ mm_generic_gsm_update_signal_quality (MMGenericGsm *self, guint32 quality) * haven't been any updates in a while. */ if (!priv->signal_quality_id) { - if (priv->signal_quality_timestamp > 0) { + if (priv->signal_emit_timestamp > 0) { time_t curtime; long int diff; curtime = time (NULL); - diff = curtime - priv->signal_quality_timestamp; + diff = curtime - priv->signal_emit_timestamp; if (diff == 0) { /* If the device is sending more than one update per second, * make sure we don't spam clients with signals. @@ -2861,11 +3369,43 @@ mm_generic_gsm_update_signal_quality (MMGenericGsm *self, guint32 quality) } } +#define CIND_TAG "+CIND:" + static void -get_signal_quality_done (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +get_cind_signal_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsmPrivate *priv; + GByteArray *indicators; + guint quality; + + info->error = mm_modem_check_removed (info->modem, error); + if (!info->error) { + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + + indicators = mm_parse_cind_query_response (response->str, &info->error); + if (indicators) { + if (indicators->len >= priv->signal_ind) { + quality = g_array_index (indicators, guint8, priv->signal_ind); + quality = CLAMP (quality, 0, 5) * 20; + mm_generic_gsm_update_signal_quality (MM_GENERIC_GSM (info->modem), quality); + mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); + } + g_byte_array_free (indicators, TRUE); + } + } + + mm_callback_info_schedule (info); +} + +static void +get_csq_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; char *reply = response->str; @@ -2918,9 +3458,13 @@ get_signal_quality (MMModemGsmNetwork *modem, 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 { + if (port) { + /* Prefer +CIND if the modem supports it, fall back to +CSQ otherwise */ + if (priv->signal_ind) + mm_at_serial_port_queue_command (port, "+CIND?", 3, get_cind_signal_done, info); + else + mm_at_serial_port_queue_command (port, "+CSQ", 3, get_csq_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); @@ -3282,7 +3826,7 @@ sms_send_done (MMAtSerialPort *port, } static void -sms_send (MMModemGsmSms *modem, +sms_send (MMModemGsmSms *modem, const char *number, const char *text, const char *smsc, @@ -3347,6 +3891,219 @@ mm_generic_gsm_get_best_at_port (MMGenericGsm *self, GError **error) return priv->secondary; } +/*****************************************************************************/ +/* MMModemGsmUssd interface */ + +static void +ussd_update_state (MMGenericGsm *self, MMModemGsmUssdState new_state) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + if (new_state != priv->ussd_state) { + priv->ussd_state = new_state; + g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_STATE); + } +} + +static void +ussd_send_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsmPrivate *priv; + gint status; + gboolean parsed = FALSE; + MMModemGsmUssdState ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE; + const char *str, *start = NULL, *end = NULL; + char *reply = NULL, *converted; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem); + ussd_state = priv->ussd_state; + + str = mm_strip_tag (response->str, "+CUSD:"); + if (!str || !isdigit (*str)) + goto done; + + status = g_ascii_digit_value (*str); + switch (status) { + case 0: /* no further action required */ + ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE; + break; + case 1: /* further action required */ + ussd_state = MM_MODEM_GSM_USSD_STATE_USER_RESPONSE; + break; + case 2: + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "USSD terminated by network."); + ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE; + break; + case 4: + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Operiation not supported."); + ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE; + break; + default: + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown USSD reply %d", status); + ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE; + break; + } + if (info->error) + goto done; + + /* look for the reply */ + if ((start = strchr (str, '"')) && (end = strrchr (str, '"')) && (start != end)) + reply = g_strndup (start + 1, end - start -1); + + if (reply) { + /* look for the reply data coding scheme */ + if ((start = strrchr (end, ',')) != NULL) + mm_dbg ("USSD data coding scheme %d", atoi (start + 1)); + + converted = mm_modem_charset_hex_to_utf8 (reply, priv->cur_charset); + mm_callback_info_set_result (info, converted, g_free); + parsed = TRUE; + g_free (reply); + } + +done: + if (!parsed && !info->error) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse USSD reply '%s'", + response->str); + } + mm_callback_info_schedule (info); + + if (info->modem) + ussd_update_state (MM_GENERIC_GSM (info->modem), ussd_state); +} + +static void +ussd_send (MMModemGsmUssd *modem, + const char *command, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *atc_command; + char *hex; + GByteArray *ussd_command = g_byte_array_new(); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + MMAtSerialPort *port; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* encode to cur_charset */ + g_warn_if_fail (mm_modem_charset_byte_array_append (ussd_command, command, FALSE, priv->cur_charset)); + /* convert to hex representation */ + hex = utils_bin2hexstr (ussd_command->data, ussd_command->len); + g_byte_array_free (ussd_command, TRUE); + atc_command = g_strdup_printf ("+CUSD=1,\"%s\",15", hex); + g_free (hex); + + mm_at_serial_port_queue_command (port, atc_command, 10, ussd_send_done, info); + g_free (atc_command); + + ussd_update_state (MM_GENERIC_GSM (modem), MM_MODEM_GSM_USSD_STATE_ACTIVE); +} + +static void +ussd_initiate (MMModemGsmUssd *modem, + const char *command, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + + if (priv->ussd_state != MM_MODEM_GSM_USSD_STATE_IDLE) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "USSD session already active."); + mm_callback_info_schedule (info); + return; + } + + ussd_send (modem, command, callback, user_data); + return; +} + +static void +ussd_respond (MMModemGsmUssd *modem, + const char *command, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + + if (priv->ussd_state != MM_MODEM_GSM_USSD_STATE_USER_RESPONSE) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "No active USSD session, cannot respond."); + mm_callback_info_schedule (info); + return; + } + + ussd_send (modem, command, callback, user_data); + return; +} + +static void +ussd_cancel_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); + + if (info->modem) + ussd_update_state (MM_GENERIC_GSM (info->modem), MM_MODEM_GSM_USSD_STATE_IDLE); +} + +static void +ussd_cancel (MMModemGsmUssd *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+CUSD=2", 10, ussd_cancel_done, info); +} + /*****************************************************************************/ /* MMModemSimple interface */ @@ -3481,6 +4238,7 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) gboolean done = FALSE; MMModemGsmAllowedMode allowed_mode; gboolean home_only = FALSE; + char *data_device; info->error = mm_modem_check_removed (modem, error); if (info->error) @@ -3488,16 +4246,9 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) 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); - } + g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL); + mm_dbg ("(%s): simple connect state %d", data_device, state); + g_free (data_device); switch (state) { case SIMPLE_STATE_CHECK_PIN: @@ -3564,7 +4315,7 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) priv->roam_allowed = !home_only; /* Don't connect if we're not supposed to be roaming */ - status = gsm_reg_status (MM_GENERIC_GSM (modem)); + status = gsm_reg_status (MM_GENERIC_GSM (modem), NULL); 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, @@ -3596,29 +4347,21 @@ simple_connect (MMModemSimple *simple, gpointer user_data) { 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); + GHashTableIter iter; + gpointer key, value; + char *data_device; + + /* List simple connect properties when debugging */ + g_object_get (G_OBJECT (simple), MM_MODEM_DATA_DEVICE, &data_device, NULL); + 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); + mm_dbg ("(%s): %s => %s", 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", @@ -3761,6 +4504,159 @@ simple_get_status (MMModemSimple *simple, /*****************************************************************************/ +static gboolean +gsm_lac_ci_available (MMGenericGsm *self, guint32 *out_idx) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMModemGsmNetworkRegStatus status; + guint idx; + + /* Must be registered, and have operator code, LAC and CI before GSM_LAC_CI is valid */ + status = gsm_reg_status (self, &idx); + if (out_idx) + *out_idx = idx; + + if ( status != MM_MODEM_GSM_NETWORK_REG_STATUS_HOME + && status != MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) + return FALSE; + + if (!priv->oper_code || !strlen (priv->oper_code)) + return FALSE; + + if (!priv->lac[idx] || !priv->cell_id[idx]) + return FALSE; + + return TRUE; +} + +static void +update_lac_ci (MMGenericGsm *self, gulong lac, gulong ci, guint idx) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + gboolean changed = FALSE; + + if (lac != priv->lac[idx]) { + priv->lac[idx] = lac; + changed = TRUE; + } + + if (ci != priv->cell_id[idx]) { + priv->cell_id[idx] = ci; + changed = TRUE; + } + + if (changed && gsm_lac_ci_available (self, NULL) && priv->loc_enabled && priv->loc_signal) + g_object_notify (G_OBJECT (self), MM_MODEM_LOCATION_LOCATION); +} + +static void +destroy_gvalue (gpointer data) +{ + GValue *value = (GValue *) data; + + g_value_unset (value); + g_slice_free (GValue, value); +} + +static GHashTable * +make_location_hash (MMGenericGsm *self, GError **error) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + GHashTable *locations = NULL; + guint32 reg_idx = 0; + GValue *val; + char mcc[4] = { 0, 0, 0, 0 }; + char mnc[4] = { 0, 0, 0, 0 }; + + if (priv->loc_caps == MM_MODEM_LOCATION_CAPABILITY_UNKNOWN) { + g_set_error_literal (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Modem has no location capabilities"); + return NULL; + } + + locations = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, destroy_gvalue); + + if (!gsm_lac_ci_available (self, ®_idx)) + return locations; + + memcpy (mcc, priv->oper_code, 3); + /* Not all modems report 6-digit MNCs */ + memcpy (mnc, priv->oper_code + 3, 2); + if (strlen (priv->oper_code) == 6) + mnc[2] = priv->oper_code[5]; + + val = g_slice_new0 (GValue); + g_value_init (val, G_TYPE_STRING); + g_value_take_string (val, g_strdup_printf ("%s,%s,%lX,%lX", + mcc, + mnc, + priv->lac[reg_idx], + priv->cell_id[reg_idx])); + g_hash_table_insert (locations, + GUINT_TO_POINTER (MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI), + val); + + return locations; +} + +static void +location_enable (MMModemLocation *modem, + gboolean loc_enable, + gboolean signal_location, + MMModemFn callback, + gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMCallbackInfo *info; + + if (loc_enable != priv->loc_enabled) { + priv->loc_enabled = loc_enable; + g_object_notify (G_OBJECT (modem), MM_MODEM_LOCATION_ENABLED); + } + + if (signal_location != priv->loc_signal) { + priv->loc_signal = signal_location; + g_object_notify (G_OBJECT (modem), MM_MODEM_LOCATION_SIGNALS_LOCATION); + } + + if (loc_enable && signal_location && gsm_lac_ci_available (self, NULL)) + g_object_notify (G_OBJECT (modem), MM_MODEM_LOCATION_LOCATION); + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_schedule (info); +} + +static void +location_get (MMModemLocation *modem, + MMModemLocationGetFn callback, + gpointer user_data) +{ + MMGenericGsm *self = MM_GENERIC_GSM (modem); + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + GHashTable *locations = NULL; + GError *error = NULL; + + if (priv->loc_caps == MM_MODEM_LOCATION_CAPABILITY_UNKNOWN) { + error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Modem has no location capabilities"); + } else if (priv->loc_enabled) + locations = make_location_hash (self, &error); + else + locations = g_hash_table_new (g_direct_hash, g_direct_equal); + + callback (modem, locations, error, user_data); + if (locations) + g_hash_table_destroy (locations); + g_clear_error (&error); +} + +/*****************************************************************************/ + static void modem_state_changed (MMGenericGsm *self, GParamSpec *pspec, gpointer user_data) { @@ -3797,6 +4693,13 @@ modem_init (MMModem *modem_class) modem_class->set_charset = set_charset; } +static void +modem_location_init (MMModemLocation *class) +{ + class->enable = location_enable; + class->get_location = location_get; +} + static void modem_gsm_card_init (MMModemGsmCard *class) { @@ -3827,6 +4730,14 @@ modem_gsm_sms_init (MMModemGsmSms *class) class->send = sms_send; } +static void +modem_gsm_ussd_init (MMModemGsmUssd *class) +{ + class->initiate = ussd_initiate; + class->respond = ussd_respond; + class->cancel = ussd_cancel; +} + static void modem_simple_init (MMModemSimple *class) { @@ -3845,12 +4756,49 @@ mm_generic_gsm_init (MMGenericGsm *self) mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_GSM_NETWORK_ALLOWED_MODE, + NULL, MM_MODEM_GSM_NETWORK_DBUS_INTERFACE); mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY, + NULL, MM_MODEM_GSM_NETWORK_DBUS_INTERFACE); + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_LOCATION_CAPABILITIES, + "Capabilities", + MM_MODEM_LOCATION_DBUS_INTERFACE); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_LOCATION_ENABLED, + "Enabled", + MM_MODEM_LOCATION_DBUS_INTERFACE); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_LOCATION_SIGNALS_LOCATION, + NULL, + MM_MODEM_LOCATION_DBUS_INTERFACE); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_LOCATION_LOCATION, + NULL, + MM_MODEM_LOCATION_DBUS_INTERFACE); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_GSM_USSD_STATE, + "State", + MM_MODEM_GSM_USSD_DBUS_INTERFACE); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION, + "NetworkNotification", + MM_MODEM_GSM_USSD_DBUS_INTERFACE); + + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_GSM_USSD_NETWORK_REQUEST, + "NetworkRequest", + MM_MODEM_GSM_USSD_DBUS_INTERFACE); + g_signal_connect (self, "notify::" MM_MODEM_STATE, G_CALLBACK (modem_state_changed), NULL); } @@ -3869,6 +4817,14 @@ set_property (GObject *object, guint prop_id, case MM_GENERIC_GSM_PROP_SUPPORTED_MODES: case MM_GENERIC_GSM_PROP_ALLOWED_MODE: case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY: + case MM_GENERIC_GSM_PROP_SIM_IDENTIFIER: + case MM_GENERIC_GSM_PROP_LOC_CAPABILITIES: + case MM_GENERIC_GSM_PROP_LOC_ENABLED: + case MM_GENERIC_GSM_PROP_LOC_SIGNAL: + case MM_GENERIC_GSM_PROP_LOC_LOCATION: + case MM_GENERIC_GSM_PROP_USSD_STATE: + case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST: + case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -3876,11 +4832,30 @@ set_property (GObject *object, guint prop_id, } } +static const char * +ussd_state_to_string (MMModemGsmUssdState ussd_state) +{ + switch (ussd_state) { + case MM_MODEM_GSM_USSD_STATE_IDLE: + return "idle"; + case MM_MODEM_GSM_USSD_STATE_ACTIVE: + return "active"; + case MM_MODEM_GSM_USSD_STATE_USER_RESPONSE: + return "user-response"; + default: + break; + } + + g_warning ("Unknown GSM USSD state %d", ussd_state); + return "unknown"; +} + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object); + GHashTable *locations = NULL; switch (prop_id) { case MM_MODEM_PROP_DATA_DEVICE: @@ -3926,6 +4901,37 @@ get_property (GObject *object, guint prop_id, else g_value_set_uint (value, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN); break; + case MM_GENERIC_GSM_PROP_SIM_IDENTIFIER: + g_value_set_string (value, priv->simid); + break; + case MM_GENERIC_GSM_PROP_LOC_CAPABILITIES: + g_value_set_uint (value, priv->loc_caps); + break; + case MM_GENERIC_GSM_PROP_LOC_ENABLED: + g_value_set_boolean (value, priv->loc_enabled); + break; + case MM_GENERIC_GSM_PROP_LOC_SIGNAL: + g_value_set_boolean (value, priv->loc_signal); + break; + case MM_GENERIC_GSM_PROP_LOC_LOCATION: + /* We don't allow property accesses unless location change signalling + * is enabled, for security reasons. + */ + if (priv->loc_enabled && priv->loc_signal) + locations = make_location_hash (MM_GENERIC_GSM (object), NULL); + else + locations = g_hash_table_new (g_direct_hash, g_direct_equal); + g_value_take_boxed (value, locations); + break; + case MM_GENERIC_GSM_PROP_USSD_STATE: + g_value_set_string (value, ussd_state_to_string (priv->ussd_state)); + break; + case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST: + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION: + g_value_set_string (value, ""); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3958,6 +4964,7 @@ finalize (GObject *object) g_free (priv->oper_code); g_free (priv->oper_name); + g_free (priv->simid); G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object); } @@ -3978,6 +4985,7 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) klass->do_enable = real_do_enable; klass->do_enable_power_up_done = real_do_enable_power_up_done; klass->do_disconnect = real_do_disconnect; + klass->get_sim_iccid = real_get_sim_iccid; /* Properties */ g_object_class_override_property (object_class, @@ -4004,6 +5012,38 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY, MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY); + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_SIM_IDENTIFIER, + MM_MODEM_GSM_CARD_SIM_IDENTIFIER); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_LOC_CAPABILITIES, + MM_MODEM_LOCATION_CAPABILITIES); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_LOC_ENABLED, + MM_MODEM_LOCATION_ENABLED); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_LOC_SIGNAL, + MM_MODEM_LOCATION_SIGNALS_LOCATION); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_LOC_LOCATION, + MM_MODEM_LOCATION_LOCATION); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_USSD_STATE, + MM_MODEM_GSM_USSD_STATE); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION, + MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST, + MM_MODEM_GSM_USSD_NETWORK_REQUEST); + 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 de9dc89..5712660 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -17,6 +17,8 @@ #ifndef MM_GENERIC_GSM_H #define MM_GENERIC_GSM_H +#include + #include "mm-modem-gsm.h" #include "mm-modem-gsm-network.h" #include "mm-modem-base.h" @@ -46,7 +48,15 @@ typedef enum { MM_GENERIC_GSM_PROP_SUPPORTED_MODES, MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL, MM_GENERIC_GSM_PROP_ALLOWED_MODE, - MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY + MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY, + MM_GENERIC_GSM_PROP_LOC_CAPABILITIES, + MM_GENERIC_GSM_PROP_LOC_ENABLED, + MM_GENERIC_GSM_PROP_LOC_SIGNAL, + MM_GENERIC_GSM_PROP_LOC_LOCATION, + MM_GENERIC_GSM_PROP_SIM_IDENTIFIER, + MM_GENERIC_GSM_PROP_USSD_STATE, + MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST, + MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION, } MMGenericGsmProp; typedef enum { @@ -110,13 +120,30 @@ typedef struct { void (*get_access_technology) (MMGenericGsm *self, MMModemUIntFn callback, gpointer user_data); + + /* Called by the generic class to get additional Location capabilities that + * subclasses may implement. The MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI + * capabilities is automatically provided by the generic class, and + * subclasses should return a bitfield of additional location capabilities + * they support in the callback here. + */ + void (*loc_get_capabilities) (MMGenericGsm *self, + MMModemUIntFn callback, + gpointer user_data); + + /* Called by the generic class to retrieve the SIM's ICCID */ + void (*get_sim_iccid) (MMGenericGsm *self, + MMModemStringFn callback, + gpointer user_data); } MMGenericGsmClass; GType mm_generic_gsm_get_type (void); MMModem *mm_generic_gsm_new (const char *device, const char *driver, - const char *plugin); + const char *plugin, + guint vendor, + guint product); /* Private, for subclasses */ diff --git a/src/mm-log.c b/src/mm-log.c new file mode 100644 index 0000000..bcf806a --- /dev/null +++ b/src/mm-log.c @@ -0,0 +1,233 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2011 Red Hat, Inc. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mm-log.h" + +enum { + TS_FLAG_NONE = 0, + TS_FLAG_WALL, + TS_FLAG_REL +}; + +static gboolean ts_flags = TS_FLAG_NONE; +static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR; +static GTimeVal rel_start = { 0, 0 }; +static int logfd = -1; + +typedef struct { + guint32 num; + const char *name; +} LogDesc; + +static const LogDesc level_descs[] = { + { LOGL_ERR, "ERR" }, + { LOGL_WARN | LOGL_ERR, "WARN" }, + { LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" }, + { LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" }, + { 0, NULL } +}; + +void +_mm_log (const char *loc, + const char *func, + guint32 level, + const char *fmt, + ...) +{ + va_list args; + char *msg; + GTimeVal tv; + char tsbuf[100] = { 0 }; + char msgbuf[512] = { 0 }; + int syslog_priority = LOG_INFO; + const char *prefix = NULL; + ssize_t ign; + + if (!(log_level & level)) + return; + + va_start (args, fmt); + msg = g_strdup_vprintf (fmt, args); + va_end (args); + + if (ts_flags == TS_FLAG_WALL) { + g_get_current_time (&tv); + snprintf (&tsbuf[0], sizeof (tsbuf), " [%09ld.%06ld]", tv.tv_sec, tv.tv_usec); + } else if (ts_flags == TS_FLAG_REL) { + time_t secs; + suseconds_t usecs; + + g_get_current_time (&tv); + secs = tv.tv_sec - rel_start.tv_sec; + usecs = tv.tv_usec - rel_start.tv_usec; + if (usecs < 0) { + secs--; + usecs += 1000000; + } + + snprintf (&tsbuf[0], sizeof (tsbuf), " [%06ld.%06ld]", secs, usecs); + } + + if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) + prefix = "debug"; + else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) + prefix = "info"; + else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) { + prefix = "warn"; + syslog_priority = LOG_WARNING; + } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { + prefix = "err"; + syslog_priority = LOG_ERR; + } else + g_warn_if_reached (); + + if (prefix) { + if (log_level & LOGL_DEBUG) + snprintf (msgbuf, sizeof (msgbuf), "<%s>%s [%s] %s(): %s\n", prefix, tsbuf, loc, func, msg); + else + snprintf (msgbuf, sizeof (msgbuf), "<%s>%s %s\n", prefix, tsbuf, msg); + + if (logfd < 0) + syslog (syslog_priority, "%s", msgbuf); + else { + ign = write (logfd, msgbuf, strlen (msgbuf)); + if (ign) {} /* whatever; really shut up about unused result */ + + fsync (logfd); /* Make sure output is dumped to disk immediately */ + } + } + + g_free (msg); +} + +static void +log_handler (const gchar *log_domain, + GLogLevelFlags level, + const gchar *message, + gpointer ignored) +{ + int syslog_priority; + ssize_t ign; + + switch (level) { + case G_LOG_LEVEL_ERROR: + syslog_priority = LOG_CRIT; + break; + case G_LOG_LEVEL_CRITICAL: + syslog_priority = LOG_ERR; + break; + case G_LOG_LEVEL_WARNING: + syslog_priority = LOG_WARNING; + break; + case G_LOG_LEVEL_MESSAGE: + syslog_priority = LOG_NOTICE; + break; + case G_LOG_LEVEL_DEBUG: + syslog_priority = LOG_DEBUG; + break; + case G_LOG_LEVEL_INFO: + default: + syslog_priority = LOG_INFO; + break; + } + + if (logfd < 0) + syslog (syslog_priority, "%s", message); + else { + ign = write (logfd, message, strlen (message)); + if (ign) {} /* whatever; really shut up about unused result */ + } +} + +gboolean +mm_log_setup (const char *level, + const char *log_file, + gboolean show_timestamps, + gboolean rel_timestamps, + GError **error) +{ + /* levels */ + if (level && strlen (level)) { + gboolean found = FALSE; + const LogDesc *diter; + + for (diter = &level_descs[0]; diter->name; diter++) { + if (!strcasecmp (diter->name, level)) { + log_level = diter->num; + found = TRUE; + break; + } + } + + if (!found) { + g_set_error (error, 0, 0, "Unknown log level '%s'", level); + return FALSE; + } + } + + if (show_timestamps) + ts_flags = TS_FLAG_WALL; + else if (rel_timestamps) + ts_flags = TS_FLAG_REL; + + /* Grab start time for relative timestamps */ + g_get_current_time (&rel_start); + + if (log_file == NULL) + openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON); + else { + logfd = open (log_file, + O_CREAT | O_APPEND | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (logfd < 0) { + g_set_error (error, 0, 0, "Failed to open log file: (%d) %s", + errno, strerror (errno)); + return FALSE; + } + } + + g_log_set_handler (G_LOG_DOMAIN, + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, + log_handler, + NULL); + + return TRUE; +} + +void +mm_log_usr1 (void) +{ +} + +void +mm_log_shutdown (void) +{ + if (logfd < 0) + closelog (); + else + close (logfd); +} + diff --git a/src/mm-log.h b/src/mm-log.h new file mode 100644 index 0000000..9b0d875 --- /dev/null +++ b/src/mm-log.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2011 Red Hat, Inc. + */ + +#ifndef MM_LOG_H +#define MM_LOG_H + +#include + +/* Log levels */ +enum { + LOGL_ERR = 0x00000001, + LOGL_WARN = 0x00000002, + LOGL_INFO = 0x00000004, + LOGL_DEBUG = 0x00000008 +}; + +#define mm_err(...) \ + _mm_log (G_STRLOC, G_STRFUNC, LOGL_ERR, ## __VA_ARGS__ ) + +#define mm_warn(...) \ + _mm_log (G_STRLOC, G_STRFUNC, LOGL_WARN, ## __VA_ARGS__ ) + +#define mm_info(...) \ + _mm_log (G_STRLOC, G_STRFUNC, LOGL_INFO, ## __VA_ARGS__ ) + +#define mm_dbg(...) \ + _mm_log (G_STRLOC, G_STRFUNC, LOGL_DEBUG, ## __VA_ARGS__ ) + +#define mm_log(level, ...) \ + _mm_log (G_STRLOC, G_STRFUNC, level, ## __VA_ARGS__ ) + +void _mm_log (const char *loc, + const char *func, + guint32 level, + const char *fmt, + ...) __attribute__((__format__ (__printf__, 4, 5))); + +gboolean mm_log_setup (const char *level, + const char *log_file, + gboolean show_ts, + gboolean rel_ts, + GError **error); + +void mm_log_usr1 (void); + +void mm_log_shutdown (void); + +#endif /* MM_LOG_H */ + diff --git a/src/mm-manager.c b/src/mm-manager.c index 1dd1902..561d427 100644 --- a/src/mm-manager.c +++ b/src/mm-manager.c @@ -24,6 +24,7 @@ #include "mm-manager.h" #include "mm-errors.h" #include "mm-plugin.h" +#include "mm-log.h" static gboolean impl_manager_enumerate_devices (MMManager *manager, GPtrArray **devices, @@ -112,9 +113,9 @@ load_plugin (const char *path) plugin = (*plugin_create_func) (); if (plugin) { g_object_weak_ref (G_OBJECT (plugin), (GWeakNotify) g_module_close, module); - g_message ("Loaded plugin %s", mm_plugin_get_name (plugin)); + mm_info ("Loaded plugin %s", mm_plugin_get_name (plugin)); } else - g_warning ("Could not load plugin %s: initialization failed", path); + mm_warn ("Could not load plugin %s: initialization failed", path); out: if (!plugin) @@ -196,7 +197,7 @@ remove_modem (MMManager *manager, MMModem *modem) device = mm_modem_get_device (modem); g_assert (device); - g_debug ("Removed modem %s", device); + mm_dbg ("Removed modem %s", device); g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); g_hash_table_remove (priv->modems, device); @@ -234,8 +235,8 @@ check_export_modem (MMManager *self, MMModem *modem) 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); + mm_dbg ("(%s/%s): outstanding support task prevents export of %s", + info->subsys, info->name, modem_physdev); goto out; } } @@ -248,19 +249,35 @@ check_export_modem (MMManager *self, MMModem *modem) /* No outstanding port tasks, so if the modem is valid we can export it */ if (mm_modem_get_valid (modem)) { - static guint32 id = 0; + static guint32 id = 0, vid = 0, pid = 0; char *path, *data_device = NULL; + GUdevDevice *physdev; + const char *subsys = 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); - g_debug ("Exported modem %s as %s", modem_physdev, path); - - g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL); - g_debug ("(%s): data port is %s", path, data_device); + mm_dbg ("Exported modem %s as %s", modem_physdev, path); + + physdev = g_udev_client_query_by_sysfs_path (priv->udev, modem_physdev); + if (physdev) + subsys = g_udev_device_get_subsystem (physdev); + + g_object_get (G_OBJECT (modem), + MM_MODEM_DATA_DEVICE, &data_device, + MM_MODEM_HW_VID, &vid, + MM_MODEM_HW_PID, &pid, + NULL); + mm_dbg ("(%s): VID 0x%04X PID 0x%04X (%s)", + path, (vid & 0xFFFF), (pid & 0xFFFF), + subsys ? subsys : "unknown"); + mm_dbg ("(%s): data port is %s", path, data_device); g_free (data_device); + if (physdev) + g_object_unref (physdev); + g_signal_emit (self, signals[DEVICE_ADDED], 0, modem); } @@ -293,7 +310,7 @@ add_modem (MMManager *manager, MMModem *modem, MMPlugin *plugin) 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); + mm_dbg ("Added modem %s", device); g_signal_connect (modem, "notify::" MM_MODEM_VALID, G_CALLBACK (modem_valid), manager); check_export_modem (manager, modem); } @@ -430,7 +447,7 @@ supports_defer_timeout (gpointer user_data) existing = find_modem_for_device (info->manager, info->physdev_path); - g_debug ("(%s): re-checking support...", info->name); + mm_dbg ("(%s): re-checking support...", info->name); try_supports_port (info->manager, MM_PLUGIN (info->cur_plugin->data), existing, @@ -462,9 +479,9 @@ try_supports_port (MMManager *manager, 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), - info->name); + mm_dbg ("(%s): (%s) deferring support check", + mm_plugin_get_name (plugin), + info->name); if (info->defer_id) g_source_remove (info->defer_id); @@ -533,22 +550,21 @@ do_grab_port (gpointer user_data) 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, - device, - info->name); + mm_info ("(%s): %s modem %s claimed port %s", + mm_plugin_get_name (info->best_plugin), + type_name, + device, + info->name); g_free (device); 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__, - mm_plugin_get_name (info->best_plugin), - info->subsys, - info->name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); + mm_warn ("plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", + mm_plugin_get_name (info->best_plugin), + info->subsys, + info->name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); modem = existing; } } @@ -597,8 +613,8 @@ supports_callback (MMPlugin *plugin, * 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); + mm_dbg ("(%s/%s): ignoring port unsupported by physical modem's plugin", + info->subsys, info->name); supports_cleanup (info->manager, info->subsys, info->name, existing); return; } @@ -615,14 +631,14 @@ supports_callback (MMPlugin *plugin, */ 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"); + mm_dbg ("(%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 { @@ -717,7 +733,7 @@ device_added (MMManager *manager, GUdevDevice *device) const char *subsys, *name, *physdev_path, *physdev_subsys; SupportsInfo *info; char *key; - gboolean found; + gboolean found, is_candidate; GUdevDevice *physdev = NULL; MMPlugin *plugin; MMModem *existing; @@ -734,6 +750,17 @@ device_added (MMManager *manager, GUdevDevice *device) if (strncmp (name, "tty", 3) == 0 && isdigit (name[3])) return; + /* Ignore devices that aren't completely configured by udev yet. If + * ModemManager is started in parallel with udev, explicitly requesting + * devices may return devices for which not all udev rules have yet been + * applied (a bug in udev/gudev). Since we often need those rules to match + * the device to a specific ModemManager driver, we need to ensure that all + * rules have been processed before handling a device. + */ + is_candidate = g_udev_device_get_property_as_boolean (device, "ID_MM_CANDIDATE"); + if (!is_candidate) + return; + if (find_modem_for_port (manager, subsys, name)) return; @@ -756,14 +783,14 @@ device_added (MMManager *manager, GUdevDevice *device) && strcmp (name, "lo") && strcmp (name, "tty") && !strstr (name, "virbr")) - g_debug ("(%s/%s): could not get port's parent device", subsys, name); + mm_dbg ("(%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); + mm_dbg ("(%s/%s): port's parent device is blacklisted", subsys, name); goto out; } @@ -772,13 +799,13 @@ device_added (MMManager *manager, GUdevDevice *device) 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); + mm_dbg ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name); goto out; } 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); + mm_dbg ("(%s/%s): could not get port's parent device sysfs path", subsys, name); goto out; } @@ -809,7 +836,7 @@ device_removed (MMManager *manager, GUdevDevice *device) MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); MMModem *modem; const char *subsys, *name; - char *key; + char *key, *modem_device; SupportsInfo *info; g_return_if_fail (device != NULL); @@ -824,6 +851,9 @@ device_removed (MMManager *manager, GUdevDevice *device) /* find_modem_for_port handles tty and net removal */ modem = find_modem_for_port (manager, subsys, name); if (modem) { + modem_device = mm_modem_get_device (modem); + mm_info ("(%s/%s): released by modem %s", subsys, name, modem_device); + g_free (modem_device); mm_modem_release_port (modem, subsys, name); return; } @@ -838,10 +868,9 @@ device_removed (MMManager *manager, GUdevDevice *device) */ const char *sysfs_path = g_udev_device_get_sysfs_path (device); - // g_debug ("Looking for a modem for removed device %s", sysfs_path); modem = find_modem_for_device (manager, sysfs_path); if (modem) { - g_debug ("Removing modem claimed by removed device %s", sysfs_path); + mm_dbg ("Removing modem claimed by removed device %s", sysfs_path); remove_modem (manager, modem); return; } diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c index 0c39d2c..ee5e1c8 100644 --- a/src/mm-modem-base.c +++ b/src/mm-modem-base.c @@ -14,6 +14,7 @@ * Copyright (C) 2009 Red Hat, Inc. */ +#include #include #include #include @@ -24,17 +25,19 @@ #include "mm-at-serial-port.h" #include "mm-qcdm-serial-port.h" #include "mm-errors.h" -#include "mm-options.h" +#include "mm-log.h" #include "mm-properties-changed-signal.h" #include "mm-callback-info.h" #include "mm-modem-helpers.h" static void modem_init (MMModem *modem_class); +static void pc_init (MMPropertiesChanged *pc_class); G_DEFINE_TYPE_EXTENDED (MMModemBase, mm_modem_base, G_TYPE_OBJECT, G_TYPE_FLAG_VALUE_ABSTRACT, - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_PROPERTIES_CHANGED, pc_init)) #define MM_MODEM_BASE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_BASE, MMModemBasePrivate)) @@ -43,15 +46,21 @@ typedef struct { char *plugin; char *device; char *equipment_ident; + char *device_ident; char *unlock_required; guint32 unlock_retries; guint32 ip_method; gboolean valid; MMModemState state; + guint vid; + guint pid; char *manf; char *model; char *revision; + char *ati; + char *ati1; + char *gsn; MMAuthProvider *authp; @@ -139,15 +148,13 @@ mm_modem_base_add_port (MMModemBase *self, if (!port) return NULL; - if (mm_options_debug ()) { - device = mm_modem_get_device (MM_MODEM (self)); + device = mm_modem_get_device (MM_MODEM (self)); + mm_dbg ("(%s) type %s claimed by %s", + name, + mm_port_type_to_name (ptype), + device); + g_free (device); - 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; @@ -156,10 +163,32 @@ mm_modem_base_add_port (MMModemBase *self, gboolean mm_modem_base_remove_port (MMModemBase *self, MMPort *port) { + MMModemBasePrivate *priv; + char *device, *key, *name; + const char *type_name, *subsys; + gboolean removed; + g_return_val_if_fail (MM_IS_MODEM_BASE (self), FALSE); g_return_val_if_fail (port != NULL, FALSE); - return g_hash_table_remove (MM_MODEM_BASE_GET_PRIVATE (self)->ports, port); + priv = MM_MODEM_BASE_GET_PRIVATE (self); + + name = g_strdup (mm_port_get_device (port)); + subsys = mm_port_subsys_to_name (mm_port_get_subsys (port)); + type_name = mm_port_type_to_name (mm_port_get_port_type (port)); + + key = get_hash_key (subsys, name); + removed = g_hash_table_remove (priv->ports, key); + if (removed) { + /* Port may have already been destroyed by removal from the hash */ + device = mm_modem_get_device (MM_MODEM (self)); + mm_dbg ("(%s) type %s removed from %s", name, type_name, device); + g_free (device); + } + g_free (key); + g_free (name); + + return removed; } void @@ -224,9 +253,9 @@ mm_modem_base_set_equipment_identifier (MMModemBase *self, const char *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); + mm_info ("Modem %s: Equipment identifier set (%s)", dbus_path, priv->equipment_ident); else - g_message ("Modem %s: Equipment identifier not set", dbus_path); + mm_warn ("Modem %s: Equipment identifier not set", dbus_path); } g_object_notify (G_OBJECT (self), MM_MODEM_EQUIPMENT_IDENTIFIER); @@ -265,9 +294,9 @@ mm_modem_base_set_unlock_required (MMModemBase *self, const char *unlock_require 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); + mm_info ("Modem %s: unlock required (%s)", dbus_path, priv->unlock_required); else - g_message ("Modem %s: unlock no longer required", dbus_path); + mm_info ("Modem %s: unlock no longer required", dbus_path); } g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_REQUIRED); @@ -301,8 +330,13 @@ mm_modem_base_set_unlock_retries (MMModemBase *self, guint 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); + if (priv->unlock_required) { + mm_info ("Modem %s: # unlock retries for %s is %d", + dbus_path, priv->unlock_required, priv->unlock_retries); + } else { + mm_info ("Modem %s: # unlock retries is %d", + dbus_path, priv->unlock_retries); + } } g_object_notify (G_OBJECT (self), MM_MODEM_UNLOCK_RETRIES); @@ -391,7 +425,7 @@ 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; + const char *manf, *cmanf, *model, *cmodel, *rev, *crev, *ati, *ati1, *gsn, *cgsn; manf = mm_callback_info_get_data (info, "card-info-manf"); cmanf = mm_callback_info_get_data (info, "card-info-c-manf"); @@ -410,6 +444,31 @@ card_info_cache_invoke (MMCallbackInfo *info) g_free (priv->revision); priv->revision = g_strdup (crev ? crev : rev); + ati = mm_callback_info_get_data (info, "card-info-ati"); + g_free (priv->ati); + priv->ati = g_strdup (ati); + + ati1 = mm_callback_info_get_data (info, "card-info-ati1"); + g_free (priv->ati1); + priv->ati1 = g_strdup (ati1); + + gsn = mm_callback_info_get_data (info, "card-info-gsn"); + cgsn = mm_callback_info_get_data (info, "card-info-c-gsn"); + g_free (priv->gsn); + priv->gsn = g_strdup (cgsn ? cgsn : gsn); + + /* Build up the device identifier */ + g_free (priv->device_ident); + priv->device_ident = mm_create_device_identifier (priv->vid, + priv->pid, + priv->ati, + priv->ati1, + priv->gsn, + priv->revision, + priv->model, + priv->manf); + g_object_notify (G_OBJECT (self), MM_MODEM_DEVICE_IDENTIFIER); + callback (info->modem, priv->manf, priv->model, priv->revision, info->error, info->user_data); } @@ -418,35 +477,44 @@ info_item_done (MMCallbackInfo *info, GString *response, GError *error, const char *tag, + const char *tag2, const char *desc) { - const char *p; + const char *p = response->str; if (!error) { - p = mm_strip_tag (response->str, tag); + if (tag) + p = mm_strip_tag (p, tag); + if (tag2) + p = mm_strip_tag (p, tag2); 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) \ +#define GET_INFO_RESP_FN(func_name, tag, tag2, desc) \ static void \ func_name (MMAtSerialPort *port, \ GString *response, \ GError *error, \ gpointer user_data) \ { \ - info_item_done ((MMCallbackInfo *) user_data, response, error, tag , desc ); \ + info_item_done ((MMCallbackInfo *) user_data, response, error, tag, tag2, 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_revision_done, "+GMR:", "AT+GMR", "card-info-revision") +GET_INFO_RESP_FN(get_model_done, "+GMM:", "AT+GMM", "card-info-model") +GET_INFO_RESP_FN(get_manf_done, "+GMI:", "AT+GMI", "card-info-manf") + +GET_INFO_RESP_FN(get_c_revision_done, "+CGMR:", "AT+CGMR", "card-info-c-revision") +GET_INFO_RESP_FN(get_c_model_done, "+CGMM:", "AT+CGMM", "card-info-c-model") +GET_INFO_RESP_FN(get_c_manf_done, "+CGMI:", "AT+CGMI", "card-info-c-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") +GET_INFO_RESP_FN(get_ati_done, NULL, "ATI", "card-info-ati") +GET_INFO_RESP_FN(get_ati1_done, NULL, "ATI1", "card-info-ati1") +GET_INFO_RESP_FN(get_gsn_done, "+GSN:", "AT+GSN", "card-info-gsn") +GET_INFO_RESP_FN(get_cgsn_done, "+CGSN:", "AT+CGSN", "card-info-c-gsn") void mm_modem_base_get_card_info (MMModemBase *self, @@ -457,7 +525,6 @@ mm_modem_base_get_card_info (MMModemBase *self, { MMModemBasePrivate *priv; MMCallbackInfo *info; - MMModemState state; gboolean cached = FALSE; GError *error = port_error; @@ -474,16 +541,8 @@ mm_modem_base_get_card_info (MMModemBase *self, */ 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."); - } - } + else if (port_error) + error = g_error_copy (port_error); /* If we have cached info or an error, don't hit up the card */ if (cached || error) { @@ -502,13 +561,19 @@ mm_modem_base_get_card_info (MMModemBase *self, G_CALLBACK (callback), user_data); - mm_callback_info_chain_start (info, 6); + mm_callback_info_chain_start (info, 10); + 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); + + mm_at_serial_port_queue_command_cached (port, "I", 3, get_ati_done, info); + mm_at_serial_port_queue_command_cached (port, "I1", 3, get_ati1_done, info); + mm_at_serial_port_queue_command_cached (port, "+GSN", 3, get_gsn_done, info); + mm_at_serial_port_queue_command_cached (port, "+CGSN", 3, get_cgsn_done, info); } /*****************************************************************************/ @@ -562,15 +627,27 @@ mm_modem_base_init (MMModemBase *self) mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_ENABLED, + NULL, MM_MODEM_DBUS_INTERFACE); mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_EQUIPMENT_IDENTIFIER, + NULL, + MM_MODEM_DBUS_INTERFACE); + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_DEVICE_IDENTIFIER, + NULL, MM_MODEM_DBUS_INTERFACE); mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_UNLOCK_REQUIRED, + NULL, MM_MODEM_DBUS_INTERFACE); mm_properties_changed_signal_register_property (G_OBJECT (self), MM_MODEM_UNLOCK_RETRIES, + NULL, + MM_MODEM_DBUS_INTERFACE); + mm_properties_changed_signal_register_property (G_OBJECT (self), + MM_MODEM_IP_METHOD, + NULL, MM_MODEM_DBUS_INTERFACE); } @@ -581,6 +658,11 @@ modem_init (MMModem *modem_class) modem_class->auth_finish = modem_auth_finish; } +static void +pc_init (MMPropertiesChanged *pc_class) +{ +} + static gboolean is_enabled (MMModemState state) { @@ -621,9 +703,18 @@ set_property (GObject *object, guint prop_id, case MM_MODEM_PROP_TYPE: case MM_MODEM_PROP_ENABLED: case MM_MODEM_PROP_EQUIPMENT_IDENTIFIER: + case MM_MODEM_PROP_DEVICE_IDENTIFIER: case MM_MODEM_PROP_UNLOCK_REQUIRED: case MM_MODEM_PROP_UNLOCK_RETRIES: break; + case MM_MODEM_PROP_HW_VID: + /* Construct only */ + priv->vid = g_value_get_uint (value); + break; + case MM_MODEM_PROP_HW_PID: + /* Construct only */ + priv->pid = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -667,12 +758,21 @@ get_property (GObject *object, guint prop_id, case MM_MODEM_PROP_EQUIPMENT_IDENTIFIER: g_value_set_string (value, priv->equipment_ident); break; + case MM_MODEM_PROP_DEVICE_IDENTIFIER: + g_value_set_string (value, priv->device_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; + case MM_MODEM_PROP_HW_VID: + g_value_set_uint (value, priv->vid); + break; + case MM_MODEM_PROP_HW_PID: + g_value_set_uint (value, priv->pid); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -692,7 +792,14 @@ finalize (GObject *object) g_free (priv->plugin); g_free (priv->device); g_free (priv->equipment_ident); + g_free (priv->device_ident); g_free (priv->unlock_required); + g_free (priv->manf); + g_free (priv->model); + g_free (priv->revision); + g_free (priv->ati); + g_free (priv->ati1); + g_free (priv->gsn); G_OBJECT_CLASS (mm_modem_base_parent_class)->finalize (object); } @@ -749,6 +856,10 @@ mm_modem_base_class_init (MMModemBaseClass *klass) MM_MODEM_PROP_EQUIPMENT_IDENTIFIER, MM_MODEM_EQUIPMENT_IDENTIFIER); + g_object_class_override_property (object_class, + MM_MODEM_PROP_DEVICE_IDENTIFIER, + MM_MODEM_DEVICE_IDENTIFIER); + g_object_class_override_property (object_class, MM_MODEM_PROP_UNLOCK_REQUIRED, MM_MODEM_UNLOCK_REQUIRED); @@ -757,6 +868,14 @@ mm_modem_base_class_init (MMModemBaseClass *klass) MM_MODEM_PROP_UNLOCK_RETRIES, MM_MODEM_UNLOCK_RETRIES); - mm_properties_changed_signal_new (object_class); + g_object_class_override_property (object_class, + MM_MODEM_PROP_HW_VID, + MM_MODEM_HW_VID); + + g_object_class_override_property (object_class, + MM_MODEM_PROP_HW_PID, + MM_MODEM_HW_PID); + + mm_properties_changed_signal_enable (object_class); } diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c index e80dc4e..722918e 100644 --- a/src/mm-modem-cdma.c +++ b/src/mm-modem-cdma.c @@ -26,6 +26,8 @@ static void impl_modem_cdma_get_signal_quality (MMModemCdma *modem, DBusGMethodI static void impl_modem_cdma_get_esn (MMModemCdma *modem, DBusGMethodInvocation *context); static void impl_modem_cdma_get_serving_system (MMModemCdma *modem, DBusGMethodInvocation *context); static void impl_modem_cdma_get_registration_state (MMModemCdma *modem, DBusGMethodInvocation *context); +static void impl_modem_cdma_activate (MMModemCdma *modem, DBusGMethodInvocation *context); +static void impl_modem_cdma_activate_manual (MMModemCdma *modem, DBusGMethodInvocation *context); #include "mm-modem-cdma-glue.h" @@ -251,6 +253,39 @@ mm_modem_cdma_emit_signal_quality_changed (MMModemCdma *self, guint32 quality) g_signal_emit (self, signals[SIGNAL_QUALITY], 0, quality); } +void mm_modem_cdma_activate(MMModemCdma *self, MMModemUIntFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_CDMA_GET_INTERFACE (self)->activate) + MM_MODEM_CDMA_GET_INTERFACE (self)->activate(self, callback, user_data); + else + uint_op_not_supported (MM_MODEM (self), callback, user_data); +} + +static void impl_modem_cdma_activate(MMModemCdma *modem, + DBusGMethodInvocation *context) +{ + mm_modem_cdma_activate (modem, uint_call_done, context); +} + + +void mm_modem_cdma_activate_manual(MMModemCdma *self, MMModemUIntFn callback, + gpointer user_data) { + g_return_if_fail (MM_IS_MODEM_CDMA (self)); + g_return_if_fail (callback != NULL); + if (MM_MODEM_CDMA_GET_INTERFACE (self)->activate_manual) + MM_MODEM_CDMA_GET_INTERFACE (self)->activate_manual(self, callback, user_data); + else + uint_op_not_supported (MM_MODEM (self), callback, user_data); +} +static void impl_modem_cdma_activate_manual (MMModemCdma *modem, + DBusGMethodInvocation *context) { + mm_modem_cdma_activate_manual(modem, uint_call_done, context); +} + /*****************************************************************************/ static void @@ -322,6 +357,15 @@ mm_modem_cdma_init (gpointer g_iface) if (initialized) return; + /* Properties */ + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_CDMA_MEID, + "MEID", + "MEID", + NULL, + G_PARAM_READABLE)); + /* Signals */ signals[SIGNAL_QUALITY] = g_signal_new ("signal-quality", diff --git a/src/mm-modem-cdma.h b/src/mm-modem-cdma.h index a7a626f..4d30386 100644 --- a/src/mm-modem-cdma.h +++ b/src/mm-modem-cdma.h @@ -35,6 +35,14 @@ typedef enum { #define MM_MODEM_CDMA_REGISTRATION_STATE_CHANGED "registration-state-changed" +#define MM_MODEM_CDMA_MEID "meid" + +typedef enum { + MM_MODEM_CDMA_PROP_FIRST = 0x1200, + + MM_MODEM_CDMA_PROP_MEID = MM_MODEM_CDMA_PROP_FIRST, +} MMModemCdmaProp; + typedef struct _MMModemCdma MMModemCdma; typedef void (*MMModemCdmaServingSystemFn) (MMModemCdma *modem, @@ -70,6 +78,18 @@ struct _MMModemCdma { MMModemCdmaRegistrationStateFn callback, gpointer user_data); + void (*activate) (MMModemCdma *self, + MMModemUIntFn callback, + gpointer user_data); + + void (*activate_manual) (MMModemCdma *self, + MMModemUIntFn callback, + gpointer user_data); + + void (*activate_manual_debug) (MMModemCdma *self, + MMModemUIntFn callback, + gpointer user_data); + /* Signals */ void (*signal_quality) (MMModemCdma *self, guint32 quality); @@ -97,6 +117,12 @@ void mm_modem_cdma_get_registration_state (MMModemCdma *self, MMModemCdmaRegistrationStateFn callback, gpointer user_data); +void mm_modem_cdma_activate (MMModemCdma *self, MMModemUIntFn callback, + gpointer user_data); + +void mm_modem_cdma_activate_manual (MMModemCdma *self, MMModemUIntFn callback, + gpointer user_data); + /* Protected */ void mm_modem_cdma_emit_signal_quality_changed (MMModemCdma *self, guint32 new_quality); diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c index e03b964..d04f014 100644 --- a/src/mm-modem-gsm-card.c +++ b/src/mm-modem-gsm-card.c @@ -579,6 +579,14 @@ mm_modem_gsm_card_init (gpointer g_iface) initialized = TRUE; + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_GSM_CARD_SIM_IDENTIFIER, + "SimIdentifier", + "An obfuscated identifier of the SIM", + NULL, + G_PARAM_READABLE)); + g_object_interface_install_property (g_iface, g_param_spec_uint (MM_MODEM_GSM_CARD_SUPPORTED_BANDS, diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h index 584d734..e617d8f 100644 --- a/src/mm-modem-gsm-card.h +++ b/src/mm-modem-gsm-card.h @@ -26,6 +26,7 @@ #define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands" #define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes" +#define MM_MODEM_GSM_CARD_SIM_IDENTIFIER "sim-identifier" #define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin" #define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2" diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c index 4cd6921..75ca7de 100644 --- a/src/mm-modem-gsm-network.c +++ b/src/mm-modem-gsm-network.c @@ -108,6 +108,8 @@ mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act) 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; + else if (act & MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS) + return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA; return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY; } diff --git a/src/mm-modem-gsm-ussd.c b/src/mm-modem-gsm-ussd.c new file mode 100644 index 0000000..f90a845 --- /dev/null +++ b/src/mm-modem-gsm-ussd.c @@ -0,0 +1,378 @@ +/* -*- 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 Guido Guenther + */ + +#include +#include + +#include "mm-modem-gsm-ussd.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-marshal.h" + +static void impl_modem_gsm_ussd_initiate(MMModemGsmUssd *modem, + const char*command, + DBusGMethodInvocation *context); + +static void impl_modem_gsm_ussd_respond(MMModemGsmUssd *modem, + const char *response, + DBusGMethodInvocation *context); + +static void impl_modem_gsm_ussd_cancel(MMModemGsmUssd *modem, + DBusGMethodInvocation *context); + +#include "mm-modem-gsm-ussd-glue.h" + + +/*****************************************************************************/ + +static void +str_call_done (MMModem *modem, const char *result, 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, result); +} + +static void +str_call_not_supported (MMModemGsmUssd *self, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_string_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; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +async_call_not_supported (MMModemGsmUssd *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); +} + +/*****************************************************************************/ + +void +mm_modem_gsm_ussd_initiate (MMModemGsmUssd *self, + const char *command, + MMModemStringFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_USSD (self)); + g_return_if_fail (command != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->initiate) + MM_MODEM_GSM_USSD_GET_INTERFACE (self)->initiate(self, command, callback, user_data); + else + str_call_not_supported (self, callback, user_data); + +} + +void +mm_modem_gsm_ussd_respond (MMModemGsmUssd *self, + const char *command, + MMModemStringFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_USSD (self)); + g_return_if_fail (command != NULL); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->respond) + MM_MODEM_GSM_USSD_GET_INTERFACE (self)->respond(self, command, callback, user_data); + else + str_call_not_supported (self, callback, user_data); + +} + +void +mm_modem_gsm_ussd_cancel (MMModemGsmUssd *self, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_GSM_USSD (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->cancel) + MM_MODEM_GSM_USSD_GET_INTERFACE (self)->cancel(self, callback, user_data); + else + async_call_not_supported (self, callback, user_data); + +} + +/*****************************************************************************/ + +typedef struct { + char *command; +} UssdAuthInfo; + +static void +ussd_auth_info_destroy (gpointer data) +{ + UssdAuthInfo *info = data; + + g_free (info->command); + g_free (info); +} + +static UssdAuthInfo * +ussd_auth_info_new (const char* command) +{ + UssdAuthInfo *info; + + info = g_malloc0 (sizeof (UssdAuthInfo)); + info->command = g_strdup (command); + + return info; +} + +/*****************************************************************************/ + +static void +ussd_initiate_auth_cb (MMAuthRequest *req, + GObject *owner, + DBusGMethodInvocation *context, + gpointer user_data) +{ + MMModemGsmUssd *self = MM_MODEM_GSM_USSD (owner); + UssdAuthInfo *info = user_data; + GError *error = NULL; + + /* Return any authorization error, otherwise initiate the USSD */ + if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) + goto done; + + if (!info->command) { + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Missing USSD command"); + } + +done: + if (error) { + str_call_done (MM_MODEM (self), NULL, error, context); + g_error_free (error); + } else + mm_modem_gsm_ussd_initiate (self, info->command, str_call_done, context); +} + +static void +impl_modem_gsm_ussd_initiate (MMModemGsmUssd *modem, + const char *command, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + UssdAuthInfo *info; + + info = ussd_auth_info_new (command); + + /* Make sure the caller is authorized to initiate the USSD */ + if (!mm_modem_auth_request (MM_MODEM (modem), + MM_AUTHORIZATION_USSD, + context, + ussd_initiate_auth_cb, + info, + ussd_auth_info_destroy, + &error)) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + +static void +ussd_respond_auth_cb (MMAuthRequest *req, + GObject *owner, + DBusGMethodInvocation *context, + gpointer user_data) +{ + MMModemGsmUssd *self = MM_MODEM_GSM_USSD (owner); + UssdAuthInfo *info = user_data; + GError *error = NULL; + + /* Return any authorization error, otherwise respond to the USSD */ + if (!mm_modem_auth_finish (MM_MODEM (self), req, &error)) + goto done; + + if (!info->command) { + error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Missing USSD command"); + } + +done: + if (error) { + str_call_done (MM_MODEM (self), NULL, error, context); + g_error_free (error); + } else + mm_modem_gsm_ussd_respond (self, info->command, str_call_done, context); +} + +static void +impl_modem_gsm_ussd_respond (MMModemGsmUssd *modem, + const char *command, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + UssdAuthInfo *info; + + info = ussd_auth_info_new (command); + + /* Make sure the caller is authorized to respond to the USSD */ + if (!mm_modem_auth_request (MM_MODEM (modem), + MM_AUTHORIZATION_USSD, + context, + ussd_respond_auth_cb, + info, + ussd_auth_info_destroy, + &error)) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + +static void +ussd_cancel_auth_cb (MMAuthRequest *req, + GObject *owner, + DBusGMethodInvocation *context, + gpointer user_data) +{ + MMModemGsmUssd *self = MM_MODEM_GSM_USSD (owner); + GError *error = NULL; + + /* Return any authorization error, otherwise cancel the USSD */ + mm_modem_auth_finish (MM_MODEM (self), req, &error); + + if (error) { + str_call_done (MM_MODEM (self), NULL, error, context); + g_error_free (error); + } else + mm_modem_gsm_ussd_cancel (self, async_call_done, context); +} + +static void +impl_modem_gsm_ussd_cancel (MMModemGsmUssd *modem, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + UssdAuthInfo *info; + + info = ussd_auth_info_new (NULL); + + /* Make sure the caller is authorized to cancel USSD */ + if (!mm_modem_auth_request (MM_MODEM (modem), + MM_AUTHORIZATION_USSD, + context, + ussd_cancel_auth_cb, + info, + ussd_auth_info_destroy, + &error)) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + +/*****************************************************************************/ + +static void +mm_modem_gsm_ussd_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (initialized) + return; + + /* Properties */ + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_GSM_USSD_STATE, + "State", + "Current state of USSD session", + NULL, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION, + "NetworkNotification", + "Network initiated request, no response required", + NULL, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_GSM_USSD_NETWORK_REQUEST, + "NetworkRequest", + "Network initiated request, reponse required", + NULL, + G_PARAM_READABLE)); + + initialized = TRUE; +} + +GType +mm_modem_gsm_ussd_get_type (void) +{ + static GType ussd_type = 0; + + if (!G_UNLIKELY (ussd_type)) { + const GTypeInfo ussd_info = { + sizeof (MMModemGsmUssd), /* class_size */ + mm_modem_gsm_ussd_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + ussd_type = g_type_register_static (G_TYPE_INTERFACE, + "MMModemGsmUssd", + &ussd_info, 0); + + g_type_interface_add_prerequisite (ussd_type, G_TYPE_OBJECT); + dbus_g_object_type_install_info (ussd_type, &dbus_glib_mm_modem_gsm_ussd_object_info); + + dbus_g_object_type_register_shadow_property (ussd_type, + "State", + MM_MODEM_GSM_USSD_STATE); + } + + return ussd_type; +} diff --git a/src/mm-modem-gsm-ussd.h b/src/mm-modem-gsm-ussd.h new file mode 100644 index 0000000..c8f652b --- /dev/null +++ b/src/mm-modem-gsm-ussd.h @@ -0,0 +1,75 @@ +/* -*- 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 Guido Guenther + */ + +#ifndef MM_MODEM_GSM_USSD_H +#define MM_MODEM_GSM_USSD_H + +#include + +typedef enum { + MM_MODEM_GSM_USSD_STATE_IDLE = 0x00000000, + MM_MODEM_GSM_USSD_STATE_ACTIVE = 0x00000001, + MM_MODEM_GSM_USSD_STATE_USER_RESPONSE = 0x00000002, +} MMModemGsmUssdState; + +#define MM_MODEM_GSM_USSD_STATE "ussd-state" +#define MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION "network-notification" +#define MM_MODEM_GSM_USSD_NETWORK_REQUEST "network-request" + +#define MM_TYPE_MODEM_GSM_USSD (mm_modem_gsm_ussd_get_type ()) +#define MM_MODEM_GSM_USSD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_USSD, MMModemGsmUssd)) +#define MM_IS_MODEM_GSM_USSD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_USSD)) +#define MM_MODEM_GSM_USSD_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_USSD, MMModemGsmUssd)) + +#define MM_MODEM_GSM_USSD_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Gsm.Ussd" + +typedef struct _MMModemGsmUssd MMModemGsmUssd; + +struct _MMModemGsmUssd { + GTypeInterface g_iface; + + /* Methods */ + void (*initiate) (MMModemGsmUssd *modem, + const char *command, + MMModemStringFn callback, + gpointer user_data); + + void (*respond) (MMModemGsmUssd *modem, + const char *command, + MMModemStringFn callback, + gpointer user_data); + + void (*cancel) (MMModemGsmUssd *modem, + MMModemFn callback, + gpointer user_data); +}; + +GType mm_modem_gsm_ussd_get_type (void); + +void mm_modem_gsm_ussd_initiate (MMModemGsmUssd *self, + const char *command, + MMModemStringFn callback, + gpointer user_data); + +void mm_modem_gsm_ussd_respond (MMModemGsmUssd *self, + const char *command, + MMModemStringFn callback, + gpointer user_data); + +void mm_modem_gsm_ussd_cancel (MMModemGsmUssd *self, + MMModemFn callback, + gpointer user_data); + +#endif /* MM_MODEM_GSM_USSD_H */ diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h index 6d9135a..a427d35 100644 --- a/src/mm-modem-gsm.h +++ b/src/mm-modem-gsm.h @@ -54,8 +54,9 @@ typedef enum { 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_HSPA_PLUS = 9, /* UTRAN w/HSPA+ */ - MM_MODEM_GSM_ACCESS_TECH_LAST = MM_MODEM_GSM_ACCESS_TECH_HSPA + MM_MODEM_GSM_ACCESS_TECH_LAST = MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS } MMModemGsmAccessTech; typedef enum { diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 6e76f46..f13b4f2 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -15,6 +15,8 @@ */ #include +#include +#include #include #include #include @@ -23,6 +25,7 @@ #include "mm-errors.h" #include "mm-modem-helpers.h" +#include "mm-log.h" const char * mm_strip_tag (const char *str, const char *cmd) @@ -240,10 +243,14 @@ mm_gsm_destroy_scan_data (gpointer data) /* +CREG: ,,,, (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})" +/* +CREG: ,,,,, (Samsung Wave S8500) */ +/* '+CREG: 2,1,000B,2816, B, C2816OK' */ +#define CREG7 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*" + GPtrArray * mm_gsm_creg_regex_get (gboolean solicited) { - GPtrArray *array = g_ptr_array_sized_new (6); + GPtrArray *array = g_ptr_array_sized_new (7); GRegex *regex; /* #1 */ @@ -294,6 +301,14 @@ mm_gsm_creg_regex_get (gboolean solicited) g_assert (regex); g_ptr_array_add (array, regex); + /* #7 */ + if (solicited) + regex = g_regex_new (CREG7 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + else + regex = g_regex_new ("\\r\\n" CREG7 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + g_assert (regex); + g_ptr_array_add (array, regex); + return array; } @@ -783,7 +798,9 @@ mm_gsm_string_to_access_tech (const char *string) /* Better technologies are listed first since modems sometimes say * stuff like "GPRS/EDGE" and that should be handled as EDGE. */ - if (strcasestr (string, "HSPA")) + if (strcasestr (string, "HSPA+")) + return MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS; + else if (strcasestr (string, "HSPA")) return MM_MODEM_GSM_ACCESS_TECH_HSPA; else if (strcasestr (string, "HSDPA/HSUPA")) return MM_MODEM_GSM_ACCESS_TECH_HSPA; @@ -803,3 +820,278 @@ mm_gsm_string_to_access_tech (const char *string) return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; } +/*************************************************************************/ + +char * +mm_create_device_identifier (guint vid, + guint pid, + const char *ati, + const char *ati1, + const char *gsn, + const char *revision, + const char *model, + const char *manf) +{ + GString *devid, *msg = NULL; + GChecksum *sum; + char *p, *ret = NULL; + char str_vid[10], str_pid[10]; + + /* Build up the device identifier */ + devid = g_string_sized_new (50); + if (ati) + g_string_append (devid, ati); + if (ati1) { + /* Only append "ATI1" if it's differnet than "ATI" */ + if (!ati || (strcmp (ati, ati1) != 0)) + g_string_append (devid, ati1); + } + if (gsn) + g_string_append (devid, gsn); + if (revision) + g_string_append (devid, revision); + if (model) + g_string_append (devid, model); + if (manf) + g_string_append (devid, manf); + + if (!strlen (devid->str)) + return NULL; + + p = devid->str; + msg = g_string_sized_new (strlen (devid->str) + 17); + + sum = g_checksum_new (G_CHECKSUM_SHA1); + + if (vid) { + snprintf (str_vid, sizeof (str_vid) - 1, "%08x", vid); + g_checksum_update (sum, (const guchar *) &str_vid[0], strlen (str_vid)); + g_string_append_printf (msg, "%08x", vid); + } + if (vid) { + snprintf (str_pid, sizeof (str_pid) - 1, "%08x", pid); + g_checksum_update (sum, (const guchar *) &str_pid[0], strlen (str_pid)); + g_string_append_printf (msg, "%08x", pid); + } + + while (*p) { + /* Strip spaces and linebreaks */ + if (!isblank (*p) && !isspace (*p) && isascii (*p)) { + g_checksum_update (sum, (const guchar *) p, 1); + g_string_append_c (msg, *p); + } + p++; + } + ret = g_strdup (g_checksum_get_string (sum)); + g_checksum_free (sum); + + mm_dbg ("Device ID source '%s'", msg->str); + mm_dbg ("Device ID '%s'", ret); + g_string_free (msg, TRUE); + + return ret; +} + +/*************************************************************************/ + +struct CindResponse { + char *desc; + guint idx; + gint min; + gint max; +}; + +static CindResponse * +cind_response_new (const char *desc, guint idx, gint min, gint max) +{ + CindResponse *r; + char *p; + + g_return_val_if_fail (desc != NULL, NULL); + g_return_val_if_fail (idx >= 0, NULL); + + r = g_malloc0 (sizeof (CindResponse)); + + /* Strip quotes */ + r->desc = p = g_malloc0 (strlen (desc) + 1); + while (*desc) { + if (*desc != '"' && !isspace (*desc)) + *p++ = tolower (*desc); + desc++; + } + + r->idx = idx; + r->max = max; + r->min = min; + return r; +} + +static void +cind_response_free (CindResponse *r) +{ + g_return_if_fail (r != NULL); + + g_free (r->desc); + memset (r, 0, sizeof (CindResponse)); + g_free (r); +} + +const char * +cind_response_get_desc (CindResponse *r) +{ + g_return_val_if_fail (r != NULL, NULL); + + return r->desc; +} + +guint +cind_response_get_index (CindResponse *r) +{ + g_return_val_if_fail (r != NULL, 0); + + return r->idx; +} + +gint +cind_response_get_min (CindResponse *r) +{ + g_return_val_if_fail (r != NULL, -1); + + return r->min; +} + +gint +cind_response_get_max (CindResponse *r) +{ + g_return_val_if_fail (r != NULL, -1); + + return r->max; +} + +#define CIND_TAG "+CIND:" + +GHashTable * +mm_parse_cind_test_response (const char *reply, GError **error) +{ + GHashTable *hash; + GRegex *r; + GMatchInfo *match_info; + guint idx = 1; + + g_return_val_if_fail (reply != NULL, NULL); + + /* Strip whitespace and response tag */ + if (g_str_has_prefix (reply, CIND_TAG)) + reply += strlen (CIND_TAG); + while (isspace (*reply)) + reply++; + + r = g_regex_new ("\\(([^,]*),\\((\\d+)[-,](\\d+)\\)", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse scan results."); + return NULL; + } + + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cind_response_free); + + if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) { + while (g_match_info_matches (match_info)) { + CindResponse *resp; + char *desc, *tmp; + gint min = 0, max = 0; + + desc = g_match_info_fetch (match_info, 1); + + tmp = g_match_info_fetch (match_info, 2); + min = atoi (tmp); + g_free (tmp); + + tmp = g_match_info_fetch (match_info, 3); + max = atoi (tmp); + g_free (tmp); + + resp = cind_response_new (desc, idx++, min, max); + if (resp) + g_hash_table_insert (hash, g_strdup (resp->desc), resp); + + g_free (desc); + + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + } + g_regex_unref (r); + + return hash; +} + +GByteArray * +mm_parse_cind_query_response(const char *reply, GError **error) +{ + GByteArray *array = NULL; + const char *p = reply; + GRegex *r = NULL; + GMatchInfo *match_info; + guint8 t = 0; + + g_return_val_if_fail (reply != NULL, NULL); + + if (!g_str_has_prefix (p, CIND_TAG)) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse the +CIND response"); + return NULL; + } + + p += strlen (CIND_TAG); + while (isspace (*p)) + p++; + + r = g_regex_new ("(\\d+)[^0-9]+", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Internal failure attempting to parse +CIND response"); + return NULL; + } + + if (!g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failure parsing the +CIND response"); + goto done; + } + + array = g_byte_array_sized_new (g_match_info_get_match_count (match_info)); + + /* Add a zero element so callers can use 1-based indexes returned by + * cind_response_get_index(). + */ + g_byte_array_append (array, &t, 1); + + while (g_match_info_matches (match_info)) { + char *str; + gulong val; + + str = g_match_info_fetch (match_info, 1); + + errno = 0; + val = strtoul (str, NULL, 10); + + t = 0; + if ((errno == 0) && (val < 255)) + t = (guint8) val; + /* FIXME: indicate errors somehow? */ + g_byte_array_append (array, &t, 1); + + g_free (str); + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + +done: + if (r) + g_regex_unref (r); + + return array; +} + diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index fb100bc..71ccaa5 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -59,5 +59,23 @@ gboolean mm_gsm_parse_cscs_support_response (const char *reply, MMModemGsmAccessTech mm_gsm_string_to_access_tech (const char *string); +char *mm_create_device_identifier (guint vid, + guint pid, + const char *ati, + const char *ati1, + const char *gsn, + const char *revision, + const char *model, + const char *manf); + +typedef struct CindResponse CindResponse; +GHashTable *mm_parse_cind_test_response (const char *reply, GError **error); +const char *cind_response_get_desc (CindResponse *r); +guint cind_response_get_index (CindResponse *r); +gint cind_response_get_min (CindResponse *r); +gint cind_response_get_max (CindResponse *r); + +GByteArray *mm_parse_cind_query_response(const char *reply, GError **error); + #endif /* MM_MODEM_HELPERS_H */ diff --git a/src/mm-modem-location.c b/src/mm-modem-location.c index 0018295..2dadd4e 100644 --- a/src/mm-modem-location.c +++ b/src/mm-modem-location.c @@ -257,7 +257,7 @@ mm_modem_location_init (gpointer g_iface) g_param_spec_boxed (MM_MODEM_LOCATION_LOCATION, "Location", "Available location information", - G_TYPE_HASH_TABLE, + MM_MODEM_LOCATION_PROP_TYPE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_interface_install_property @@ -267,7 +267,8 @@ mm_modem_location_init (gpointer g_iface) "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_GSM_LAC_CI + | MM_MODEM_LOCATION_CAPABILITY_GPS_RAW, MM_MODEM_LOCATION_CAPABILITY_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); diff --git a/src/mm-modem-location.h b/src/mm-modem-location.h index dd622f1..7b8399f 100644 --- a/src/mm-modem-location.h +++ b/src/mm-modem-location.h @@ -23,6 +23,10 @@ #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_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Location" + +#define MM_MODEM_LOCATION_PROP_TYPE (dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, G_TYPE_VALUE)) + #define MM_MODEM_LOCATION_CAPABILITIES "location-capabilities" #define MM_MODEM_LOCATION_ENABLED "location-enabled" #define MM_MODEM_LOCATION_SIGNALS_LOCATION "signals-location" @@ -32,8 +36,9 @@ 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_GPS_RAW = 0x00000004, - MM_MODEM_LOCATION_CAPABILITY_LAST = MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI + MM_MODEM_LOCATION_CAPABILITY_LAST = MM_MODEM_LOCATION_CAPABILITY_GPS_RAW } MMModemLocationCapabilities; typedef struct _MMModemLocation MMModemLocation; diff --git a/src/mm-modem.c b/src/mm-modem.c index 221c9ea..f823472 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -18,7 +18,7 @@ #include #include #include "mm-modem.h" -#include "mm-options.h" +#include "mm-log.h" #include "mm-errors.h" #include "mm-callback-info.h" #include "mm-marshal.h" @@ -28,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_reset (MMModem *modem, DBusGMethodInvocation *context); static void impl_modem_factory_reset (MMModem *modem, const char *code, DBusGMethodInvocation *context); #include "mm-modem-glue.h" @@ -477,6 +478,56 @@ impl_modem_get_info (MMModem *modem, /*****************************************************************************/ +static void +reset_auth_cb (MMAuthRequest *req, + GObject *owner, + DBusGMethodInvocation *context, + gpointer user_data) +{ + MMModem *self = MM_MODEM (owner); + 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_reset (self, async_call_done, context); +} + +static void +impl_modem_reset (MMModem *modem, 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, + reset_auth_cb, + NULL, NULL, + &error)) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + +void +mm_modem_reset (MMModem *self, + MMModemFn callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM (self)); + g_return_if_fail (callback != NULL); + + if (MM_MODEM_GET_INTERFACE (self)->reset) + MM_MODEM_GET_INTERFACE (self)->reset (self, callback, user_data); + else + async_op_not_supported (self, callback, user_data); +} + +/*****************************************************************************/ + static void factory_reset_auth_cb (MMAuthRequest *req, GObject *owner, @@ -704,22 +755,10 @@ mm_modem_set_state (MMModem *self, dbus_path = (const char *) g_object_get_data (G_OBJECT (self), DBUS_PATH_TAG); if (dbus_path) { - 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)); - } + mm_info ("Modem %s: state changed (%s -> %s)", + dbus_path, + state_to_string (old_state), + state_to_string (new_state)); } } } @@ -832,7 +871,7 @@ mm_modem_init (gpointer g_iface) MM_MODEM_IP_METHOD_PPP, MM_MODEM_IP_METHOD_DHCP, MM_MODEM_IP_METHOD_PPP, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, @@ -868,6 +907,14 @@ mm_modem_init (gpointer g_iface) NULL, G_PARAM_READABLE)); + g_object_interface_install_property + (g_iface, + g_param_spec_string (MM_MODEM_DEVICE_IDENTIFIER, + "DeviceIdentifier", + "A best-effort identifer of the device", + NULL, + G_PARAM_READABLE)); + g_object_interface_install_property (g_iface, g_param_spec_string (MM_MODEM_UNLOCK_REQUIRED, @@ -885,6 +932,22 @@ mm_modem_init (gpointer g_iface) 0, G_MAXUINT32, 0, G_PARAM_READABLE)); + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_HW_VID, + "Hardware vendor ID", + "Hardware vendor ID", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_interface_install_property + (g_iface, + g_param_spec_uint (MM_MODEM_HW_PID, + "Hardware product ID", + "Hardware product ID", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* Signals */ g_signal_new ("state-changed", iface_type, diff --git a/src/mm-modem.h b/src/mm-modem.h index 0915180..7f0bf58 100644 --- a/src/mm-modem.h +++ b/src/mm-modem.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_MODEM_H @@ -40,7 +40,9 @@ typedef enum { } MMModemState; typedef enum { - MM_MODEM_STATE_REASON_NONE = 0 + MM_MODEM_STATE_REASON_NONE = 0, + MM_MODEM_STATE_REASON_USER_REQUESTED, + MM_MODEM_STATE_REASON_SUSPEND } MMModemStateReason; #define DBUS_PATH_TAG "dbus-path" @@ -59,11 +61,14 @@ typedef enum { #define MM_MODEM_IP_METHOD "ip-method" #define MM_MODEM_ENABLED "enabled" #define MM_MODEM_EQUIPMENT_IDENTIFIER "equipment-identifier" +#define MM_MODEM_DEVICE_IDENTIFIER "device-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 */ +#define MM_MODEM_HW_VID "hw-vid" /* not exported */ +#define MM_MODEM_HW_PID "hw-pid" /* not exported */ #define MM_MODEM_TYPE_UNKNOWN 0 #define MM_MODEM_TYPE_GSM 1 @@ -87,7 +92,10 @@ typedef enum { MM_MODEM_PROP_ENABLED, MM_MODEM_PROP_EQUIPMENT_IDENTIFIER, MM_MODEM_PROP_UNLOCK_REQUIRED, - MM_MODEM_PROP_UNLOCK_RETRIES + MM_MODEM_PROP_UNLOCK_RETRIES, + MM_MODEM_PROP_DEVICE_IDENTIFIER, + MM_MODEM_PROP_HW_VID, /* Not exported */ + MM_MODEM_PROP_HW_PID /* Not exported */ } MMModemProp; typedef struct _MMModem MMModem; @@ -188,6 +196,10 @@ struct _MMModem { MMAuthRequest *req, GError **error); + void (*reset) (MMModem *self, + MMModemFn callback, + gpointer user_data); + void (*factory_reset) (MMModem *self, const char *code, MMModemFn callback, @@ -251,6 +263,10 @@ void mm_modem_set_charset (MMModem *self, MMModemFn callback, gpointer user_data); +void mm_modem_reset (MMModem *self, + MMModemFn callback, + gpointer user_data); + void mm_modem_factory_reset (MMModem *self, const char *code, MMModemFn callback, diff --git a/src/mm-options.c b/src/mm-options.c deleted file mode 100644 index 7bbeefd..0000000 --- a/src/mm-options.c +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- 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 Novell, Inc. - */ - -#include -#include -#include "mm-options.h" - -static gboolean debug = FALSE; - -void -mm_options_parse (int argc, char *argv[]) -{ - GOptionContext *opt_ctx; - GError *error = NULL; - GOptionEntry entries[] = { - { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, - { NULL } - }; - - opt_ctx = g_option_context_new (NULL); - g_option_context_set_summary (opt_ctx, "DBus system service to communicate with modems."); - g_option_context_add_main_entries (opt_ctx, entries, NULL); - - if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { - g_warning ("%s\n", error->message); - g_error_free (error); - exit (1); - } - - g_option_context_free (opt_ctx); -} - -void -mm_options_set_debug (gboolean enabled) -{ - debug = enabled; -} - -gboolean -mm_options_debug (void) -{ - return debug; -} diff --git a/src/mm-options.h b/src/mm-options.h deleted file mode 100644 index ce33e27..0000000 --- a/src/mm-options.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -*- 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 Novell, Inc. - */ - -#ifndef MM_OPTIONS_H -#define MM_OPTIONS_H - -void mm_options_parse (int argc, char *argv[]); -void mm_options_set_debug (gboolean enabled); -gboolean mm_options_debug (void); - -#endif /* MM_OPTIONS_H */ diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c index 80d0f90..8d32e2a 100644 --- a/src/mm-plugin-base.c +++ b/src/mm-plugin-base.c @@ -18,6 +18,8 @@ #include #include + +#include #include #include @@ -34,6 +36,7 @@ #include "mm-utils.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/utils.h" +#include "mm-log.h" static void plugin_init (MMPlugin *plugin_class); @@ -48,6 +51,8 @@ G_DEFINE_TYPE_EXTENDED (MMPluginBase, mm_plugin_base, G_TYPE_OBJECT, */ static GHashTable *cached_caps = NULL; +/* Virtual port corresponding to the embeded modem */ +static gchar *virtual_port[] = {"smd0", NULL}; typedef struct { char *name; @@ -803,9 +808,9 @@ try_open (gpointer user_data) 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_dbg ("(%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 (MM_SERIAL_PORT (task_priv->probe_port), 100, TRUE, flash_done, task); } @@ -890,8 +895,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, guint16 *product) { MMPluginBasePrivate *priv; - GUdevDevice *device = NULL; - const char *vid, *pid; + GUdevDevice *device = NULL, *parent = NULL; + const char *vid = NULL, *pid = NULL, *parent_subsys; gboolean success = FALSE; g_return_val_if_fail (self != NULL, FALSE); @@ -909,8 +914,37 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, if (!device) goto out; - vid = g_udev_device_get_property (device, "ID_VENDOR_ID"); - if (!vid || (strlen (vid) != 4)) + parent = g_udev_device_get_parent (device); + if (parent) { + parent_subsys = g_udev_device_get_subsystem (parent); + if (parent_subsys) { + if (!strcmp (parent_subsys, "bluetooth")) { + /* Bluetooth devices report the VID/PID of the BT adapter here, + * which isn't really what we want. Just return null IDs instead. + */ + success = TRUE; + goto out; + } else if (!strcmp (parent_subsys, "pcmcia")) { + /* For PCMCIA devices we need to grab the PCMCIA subsystem's + * manfid and cardid, since any IDs on the tty device itself + * may be from PCMCIA controller or something else. + */ + vid = g_udev_device_get_sysfs_attr (parent, "manf_id"); + pid = g_udev_device_get_sysfs_attr (parent, "card_id"); + if (!vid || !pid) + goto out; + } + } + } + + if (!vid) + vid = g_udev_device_get_property (device, "ID_VENDOR_ID"); + if (!vid) + goto out; + + if (strncmp (vid, "0x", 2) == 0) + vid += 2; + if (strlen (vid) != 4) goto out; if (vendor) { @@ -918,8 +952,16 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, *vendor |= (guint16) ((utils_hex2byte (vid) & 0xFF) << 8); } - pid = g_udev_device_get_property (device, "ID_MODEL_ID"); - if (!pid || (strlen (pid) != 4)) { + if (!pid) + pid = g_udev_device_get_property (device, "ID_MODEL_ID"); + if (!pid) { + *vendor = 0; + goto out; + } + + if (strncmp (pid, "0x", 2) == 0) + pid += 2; + if (strlen (pid) != 4) { *vendor = 0; goto out; } @@ -934,6 +976,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, out: if (device) g_object_unref (device); + if (parent) + g_object_unref (parent); return success; } @@ -980,6 +1024,20 @@ get_driver_name (GUdevDevice *device) return ret; } +static gboolean +device_file_exists(const char *name) +{ + char *devfile; + struct stat s; + int result; + + devfile = g_strdup_printf ("/dev/%s", name); + result = stat (devfile, &s); + g_free (devfile); + + return (0 == result) ? TRUE : FALSE; +} + static MMPluginSupportsResult supports_port (MMPlugin *plugin, const char *subsys, @@ -995,6 +1053,7 @@ supports_port (MMPlugin *plugin, char *driver = NULL, *key = NULL; MMPluginBaseSupportsTask *task; MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + int idx; key = get_key (subsys, name); task = g_hash_table_lookup (priv->tasks, key); @@ -1007,6 +1066,19 @@ supports_port (MMPlugin *plugin, if (!port) goto out; + // Detect any modems accessible through the list of virtual ports + for (idx = 0; virtual_port[idx]; idx++) { + if (strcmp(name, virtual_port[idx])) + continue; + if (!device_file_exists(virtual_port[idx])) + continue; + + task = supports_task_new (self, port, physdev_path, "virtual", callback, callback_data); + g_assert (task); + g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task)); + goto find_plugin; + } + driver = get_driver_name (port); if (!driver) goto out; @@ -1015,6 +1087,7 @@ supports_port (MMPlugin *plugin, g_assert (task); g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task)); +find_plugin: 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 diff --git a/src/mm-port.c b/src/mm-port.c index b2018fe..a1291d0 100644 --- a/src/mm-port.c +++ b/src/mm-port.c @@ -19,7 +19,7 @@ #include #include "mm-port.h" -#include "mm-options.h" +#include "mm-log.h" G_DEFINE_TYPE (MMPort, mm_port, G_TYPE_OBJECT) @@ -46,6 +46,20 @@ typedef struct { /*****************************************************************************/ +const char * +mm_port_subsys_to_name (MMPortSubsys psubsys) +{ + switch (psubsys) { + case MM_PORT_SUBSYS_TTY: + return "tty"; + case MM_PORT_SUBSYS_NET: + return "net"; + default: + break; + } + return "(unknown)"; +} + const char * mm_port_type_to_name (MMPortType ptype) { @@ -161,16 +175,10 @@ 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"); - } + + mm_dbg ("(%s): port now %s", + priv->device, + connected ? "connected" : "disconnected"); } } diff --git a/src/mm-port.h b/src/mm-port.h index 43f78f4..4bcffd4 100644 --- a/src/mm-port.h +++ b/src/mm-port.h @@ -78,5 +78,7 @@ void mm_port_set_connected (MMPort *self, gboolean connected); const char * mm_port_type_to_name (MMPortType ptype); +const char * mm_port_subsys_to_name (MMPortSubsys psubsys); + #endif /* MM_PORT_H */ diff --git a/src/mm-properties-changed-signal.c b/src/mm-properties-changed-signal.c index b7f3672..4408e80 100644 --- a/src/mm-properties-changed-signal.c +++ b/src/mm-properties-changed-signal.c @@ -11,7 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2008 - 2009 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. */ #include @@ -20,17 +20,28 @@ #include #include "mm-marshal.h" #include "mm-properties-changed-signal.h" +#include "mm-properties-changed-glue.h" +#include "mm-log.h" -#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) +#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) +#define DBUS_TYPE_G_ARRAY_OF_STRING (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING)) -#define PC_SIGNAL_NAME "mm-properties-changed" +#define MM_PC_SIGNAL_NAME "mm-properties-changed" +#define DBUS_PC_SIGNAL_NAME "properties-changed" #define MM_DBUS_PROPERTY_CHANGED "MM_DBUS_PROPERTY_CHANGED" +/*****************************************************************************/ + +typedef struct { + char *real_property; + char *interface; +} ChangeInfo; + typedef struct { /* Whitelist of GObject property names for which changes will be emitted * over the bus. * - * Mapping of {property-name -> dbus-interface} + * Mapping of {property-name -> ChangeInfo} */ GHashTable *registered; @@ -42,7 +53,6 @@ typedef struct { */ GHashTable *hash; - gulong signal_id; guint idle_id; } PropertiesChangedInfo; @@ -55,6 +65,17 @@ destroy_value (gpointer data) g_slice_free (GValue, val); } +static void +change_info_free (gpointer data) +{ + ChangeInfo *info = data; + + g_free (info->real_property); + g_free (info->interface); + memset (info, 0, sizeof (ChangeInfo)); + g_free (info); +} + static PropertiesChangedInfo * properties_changed_info_new (void) { @@ -62,7 +83,7 @@ properties_changed_info_new (void) info = g_slice_new0 (PropertiesChangedInfo); - info->registered = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + info->registered = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, change_info_free); info->hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_hash_table_destroy); @@ -124,20 +145,23 @@ properties_changed (gpointer data) while (g_hash_table_iter_next (&iter, &key, &value)) { const char *interface = (const char *) key; GHashTable *props = (GHashTable *) value; + GPtrArray *ignore = g_ptr_array_new (); #ifdef DEBUG { char buf[2048] = { 0, }; g_hash_table_foreach (props, add_to_string, &buf); - g_message ("%s: %s -> (%s) %s", __func__, - G_OBJECT_TYPE_NAME (object), - interface, - buf); + mm_dbg ("%s: %s -> (%s) %s", __func__, + G_OBJECT_TYPE_NAME (object), + interface, + buf); } #endif /* Send the PropertiesChanged signal */ - g_signal_emit (object, info->signal_id, 0, interface, props); + g_signal_emit_by_name (object, MM_PC_SIGNAL_NAME, interface, props); + g_signal_emit_by_name (object, DBUS_PC_SIGNAL_NAME, interface, props, ignore); + g_ptr_array_free (ignore, TRUE); } g_hash_table_remove_all (info->hash); @@ -191,8 +215,6 @@ get_properties_changed_info (GObject *object) if (!info) { info = properties_changed_info_new (); g_object_set_data_full (object, MM_DBUS_PROPERTY_CHANGED, info, properties_changed_info_destroy); - info->signal_id = g_signal_lookup (PC_SIGNAL_NAME, G_OBJECT_TYPE (object)); - g_assert (info->signal_id); } g_assert (info); @@ -204,23 +226,23 @@ notify (GObject *object, GParamSpec *pspec) { GHashTable *interfaces; PropertiesChangedInfo *info; - const char *interface; + ChangeInfo *ch_info; GValue *value; info = get_properties_changed_info (object); - interface = g_hash_table_lookup (info->registered, pspec->name); - if (!interface) + ch_info = g_hash_table_lookup (info->registered, pspec->name); + if (!ch_info) return; /* Check if there are other changed properties for this interface already, * otherwise create a new hash table for all changed properties for this * D-Bus interface. */ - interfaces = g_hash_table_lookup (info->hash, interface); + interfaces = g_hash_table_lookup (info->hash, ch_info->interface); if (!interfaces) { interfaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_value); - g_hash_table_insert (info->hash, g_strdup (interface), interfaces); + g_hash_table_insert (info->hash, g_strdup (ch_info->interface), interfaces); } /* Now put the changed property value into the hash table of changed values @@ -229,7 +251,9 @@ notify (GObject *object, GParamSpec *pspec) value = g_slice_new0 (GValue); g_value_init (value, pspec->value_type); g_object_get_property (object, pspec->name, value); - g_hash_table_insert (interfaces, uscore_to_wincaps (pspec->name), value); + + /* Use real property name, which takes shadow properties into accound */ + g_hash_table_insert (interfaces, uscore_to_wincaps (ch_info->real_property), value); if (!info->idle_id) info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, properties_changed, object, idle_id_reset); @@ -237,11 +261,12 @@ notify (GObject *object, GParamSpec *pspec) void mm_properties_changed_signal_register_property (GObject *object, - const char *property, + const char *gobject_property, + const char *real_property, const char *interface) { PropertiesChangedInfo *info; - const char *tmp; + ChangeInfo *ch_info; /* All exported properties need to be registered explicitly for now since * dbus-glib doesn't expose any method to find out the properties registered @@ -249,28 +274,79 @@ mm_properties_changed_signal_register_property (GObject *object, */ info = get_properties_changed_info (object); - tmp = g_hash_table_lookup (info->registered, property); - if (tmp) { + ch_info = g_hash_table_lookup (info->registered, gobject_property); + if (ch_info) { g_warning ("%s: property '%s' already registerd on interface '%s'", - __func__, property, tmp); - } else - g_hash_table_insert (info->registered, g_strdup (property), g_strdup (interface)); + __func__, gobject_property, ch_info->interface); + } else { + ch_info = g_malloc0 (sizeof (ChangeInfo)); + ch_info->real_property = g_strdup (real_property ? real_property : gobject_property); + ch_info->interface = g_strdup (interface); + g_hash_table_insert (info->registered, g_strdup (gobject_property), ch_info); + } } -guint -mm_properties_changed_signal_new (GObjectClass *object_class) +void +mm_properties_changed_signal_enable (GObjectClass *object_class) { - guint id; - object_class->notify = notify; +} + +/*****************************************************************************/ - id = g_signal_new (PC_SIGNAL_NAME, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - mm_marshal_VOID__STRING_BOXED, - G_TYPE_NONE, 2, G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT); +static void +mm_properties_changed_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; - return id; + if (initialized) + return; + + g_signal_new (MM_PC_SIGNAL_NAME, + G_TYPE_FROM_INTERFACE (g_iface), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + mm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, 2, G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT); + + g_signal_new (DBUS_PC_SIGNAL_NAME, + G_TYPE_FROM_INTERFACE (g_iface), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + mm_marshal_VOID__STRING_BOXED_BOXED, + G_TYPE_NONE, 3, + G_TYPE_STRING, + DBUS_TYPE_G_MAP_OF_VARIANT, + DBUS_TYPE_G_ARRAY_OF_STRING); + + initialized = TRUE; } +GType +mm_properties_changed_get_type (void) +{ + static GType pc_type = 0; + + if (!G_UNLIKELY (pc_type)) { + const GTypeInfo pc_info = { + sizeof (MMPropertiesChanged), /* class_size */ + mm_properties_changed_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + pc_type = g_type_register_static (G_TYPE_INTERFACE, + "MMPropertiesChanged", + &pc_info, 0); + + g_type_interface_add_prerequisite (pc_type, G_TYPE_OBJECT); + dbus_g_object_type_install_info (pc_type, &dbus_glib_mm_properties_changed_object_info); + } + + return pc_type; +} diff --git a/src/mm-properties-changed-signal.h b/src/mm-properties-changed-signal.h index 60e71b9..a6d0f3e 100644 --- a/src/mm-properties-changed-signal.h +++ b/src/mm-properties-changed-signal.h @@ -11,7 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2007 - 2008 Novell, Inc. - * Copyright (C) 2008 - 2009 Red Hat, Inc. + * Copyright (C) 2008 - 2010 Red Hat, Inc. */ #ifndef _MM_PROPERTIES_CHANGED_SIGNAL_H_ @@ -19,10 +19,22 @@ #include -guint mm_properties_changed_signal_new (GObjectClass *object_class); +#define MM_TYPE_PROPERTIES_CHANGED (mm_properties_changed_get_type ()) +#define MM_PROPERTIES_CHANGED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PROPERTIES_CHANGED, MMPropertiesChanged)) +#define MM_IS_PROPERTIES_CHANGED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PROPERTIES_CHANGED)) +#define MM_PROPERTIES_CHANGED_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_PROPERTIES_CHANGED, MMPropertiesChanged)) + +typedef struct { + GTypeInterface g_iface; +} MMPropertiesChanged; + +GType mm_properties_changed_get_type (void); + +void mm_properties_changed_signal_enable (GObjectClass *object_class); void mm_properties_changed_signal_register_property (GObject *object, - const char *property, + const char *gobject_property, + const char *real_property, const char *interface); #endif /* _MM_PROPERTIES_CHANGED_SIGNAL_H_ */ diff --git a/src/mm-qcdm-serial-port.c b/src/mm-qcdm-serial-port.c index 1ce27e7..e467f2a 100644 --- a/src/mm-qcdm-serial-port.c +++ b/src/mm-qcdm-serial-port.c @@ -21,9 +21,9 @@ #include "mm-qcdm-serial-port.h" #include "mm-errors.h" -#include "mm-options.h" #include "libqcdm/src/com.h" #include "libqcdm/src/utils.h" +#include "mm-log.h" G_DEFINE_TYPE (MMQcdmSerialPort, mm_qcdm_serial_port, MM_TYPE_SERIAL_PORT) @@ -37,22 +37,38 @@ typedef struct { /*****************************************************************************/ static gboolean -parse_response (MMSerialPort *port, GByteArray *response, GError **error) +find_qcdm_start (GByteArray *response, gsize *start) { - int i; + int i, last = -1; - /* Look for the QCDM packet termination character; if we found it, treat - * the buffer as a qcdm command. + /* Look for 3 bytes and a QCDM frame marker, ie enough data for a valid + * frame. There will usually be three cases here; (1) a QCDM frame + * starting with data and terminated by 0x7E, and (2) a QCDM frame starting + * with 0x7E and ending with 0x7E, and (3) a non-QCDM frame that still + * uses HDLC framing (like Sierra CnS) that starts and ends with 0x7E. */ for (i = 0; i < response->len; i++) { - if (response->data[i] == 0x7E) - return TRUE; + if (response->data[i] == 0x7E) { + if (i > last + 3) { + /* Got a full QCDM frame; 3 non-0x7E bytes and a terminator */ + if (start) + *start = last + 1; + return TRUE; + } + + /* Save position of the last QCDM frame marker */ + last = i; + } } - - /* Otherwise, need more data from the device */ return FALSE; } +static gboolean +parse_response (MMSerialPort *port, GByteArray *response, GError **error) +{ + return find_qcdm_start (response, NULL); +} + static gsize handle_response (MMSerialPort *port, GByteArray *response, @@ -64,41 +80,49 @@ handle_response (MMSerialPort *port, GByteArray *unescaped = NULL; GError *dm_error = NULL; gsize used = 0; + gsize start = 0; + gboolean success = FALSE, more = FALSE; + gsize unescaped_len = 0; + + if (error) + goto callback; + + /* Get the offset into the buffer of where the QCDM frame starts */ + if (!find_qcdm_start (response, &start)) { + g_set_error_literal (&dm_error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to parse QCDM packet."); + /* Discard the unparsable data */ + used = response->len; + goto callback; + } - /* 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; - } + /* FIXME: don't munge around with byte array internals */ + unescaped = g_byte_array_sized_new (1024); + success = dm_decapsulate_buffer ((const char *) (response->data + start), + response->len - start, + (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; } +callback: response_callback (MM_QCDM_SERIAL_PORT (port), unescaped, dm_error ? dm_error : error, @@ -106,8 +130,9 @@ handle_response (MMSerialPort *port, if (unescaped) g_byte_array_free (unescaped, TRUE); + g_clear_error (&dm_error); - return used; + return start + used; } /*****************************************************************************/ @@ -157,7 +182,6 @@ 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); @@ -167,12 +191,7 @@ debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len) 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); + mm_dbg ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str); g_string_truncate (debug, 0); } @@ -196,6 +215,23 @@ mm_qcdm_serial_port_new (const char *name, MMPortType ptype) NULL)); } +MMQcdmSerialPort * +mm_qcdm_serial_port_new_fd (int fd, MMPortType ptype) +{ + MMQcdmSerialPort *port; + char *name; + + name = g_strdup_printf ("port%d", fd); + port = 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, + MM_SERIAL_PORT_FD, fd, + NULL)); + g_free (name); + return port; +} + static void mm_qcdm_serial_port_init (MMQcdmSerialPort *self) { diff --git a/src/mm-qcdm-serial-port.h b/src/mm-qcdm-serial-port.h index e70e124..2786ee8 100644 --- a/src/mm-qcdm-serial-port.h +++ b/src/mm-qcdm-serial-port.h @@ -50,6 +50,8 @@ GType mm_qcdm_serial_port_get_type (void); MMQcdmSerialPort *mm_qcdm_serial_port_new (const char *name, MMPortType ptype); +MMQcdmSerialPort *mm_qcdm_serial_port_new_fd (int fd, MMPortType ptype); + void mm_qcdm_serial_port_queue_command (MMQcdmSerialPort *self, GByteArray *command, guint32 timeout_seconds, diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index 7c9598e..75bcce4 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -19,6 +19,7 @@ #include "mm-serial-parsers.h" #include "mm-errors.h" +#include "mm-log.h" /* Clean up the response by removing control characters like etc */ static void @@ -33,6 +34,13 @@ response_clean (GString *response) s -= 2; } + /* Contains duplicate '' */ + s = response->str; + while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\r')) { + g_string_erase (response, 0, 1); + s = response->str; + } + /* Starts with one or more '' */ s = response->str; while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\n')) { @@ -167,7 +175,7 @@ mm_serial_parser_v0_parse (gpointer data, response_clean (response); if (local_error) { - g_debug ("Got failure code %d: %s", local_error->code, local_error->message); + mm_dbg ("Got failure code %d: %s", local_error->code, local_error->message); g_propagate_error (error, local_error); } @@ -192,6 +200,7 @@ typedef struct { GRegex *regex_connect; GRegex *regex_cme_error; GRegex *regex_cme_error_str; + GRegex *regex_ezx_error; GRegex *regex_unknown_error; GRegex *regex_connect_failed; } MMSerialParserV1; @@ -208,6 +217,7 @@ mm_serial_parser_v1_new (void) parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL); parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR: ([^\\n\\r]+)\\r\\n$", flags, 0, NULL); + parser->regex_ezx_error = g_regex_new ("\\r\\n\\MODEM ERROR: (\\d+)\\r\\n$", flags, 0, NULL); parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL); parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL); @@ -273,6 +283,19 @@ mm_serial_parser_v1_parse (gpointer data, goto done; } + /* Motorola EZX errors */ + found = g_regex_match_full (parser->regex_ezx_error, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + g_assert (str); + local_error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN); + g_free (str); + g_match_info_free (match_info); + goto done; + } + /* Last resort; unknown error */ found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, @@ -314,7 +337,7 @@ done: response_clean (response); if (local_error) { - g_debug ("Got failure code %d: %s", local_error->code, local_error->message); + mm_dbg ("Got failure code %d: %s", local_error->code, local_error->message); g_propagate_error (error, local_error); } @@ -332,6 +355,7 @@ mm_serial_parser_v1_destroy (gpointer data) g_regex_unref (parser->regex_connect); g_regex_unref (parser->regex_cme_error); g_regex_unref (parser->regex_cme_error_str); + g_regex_unref (parser->regex_ezx_error); 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 ed44167..bf2a98a 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -26,10 +26,11 @@ #include #include #include +#include #include "mm-serial-port.h" #include "mm-errors.h" -#include "mm-options.h" +#include "mm-log.h" static gboolean mm_serial_port_queue_process (gpointer data); @@ -42,6 +43,7 @@ enum { PROP_PARITY, PROP_STOPBITS, PROP_SEND_DELAY, + PROP_FD, LAST_PROP }; @@ -150,10 +152,10 @@ mm_serial_port_print_config (MMSerialPort *port, const char *detail) return; } - g_message ("*** %s (%s): (%s) baud rate: %d (%s)", - __func__, detail, mm_port_get_device (MM_PORT (port)), - stbuf.c_cflag & CBAUD, - baud_to_string (stbuf.c_cflag & CBAUD)); + mm_info ("(%s): (%s) baud rate: %d (%s)", + detail, mm_port_get_device (MM_PORT (port)), + stbuf.c_cflag & CBAUD, + baud_to_string (stbuf.c_cflag & CBAUD)); } #endif @@ -348,7 +350,7 @@ serial_debug (MMSerialPort *self, const char *prefix, const char *buf, gsize len { g_return_if_fail (len > 0); - if (mm_options_debug () && MM_SERIAL_PORT_GET_CLASS (self)->debug_log) + if (MM_SERIAL_PORT_GET_CLASS (self)->debug_log) MM_SERIAL_PORT_GET_CLASS (self)->debug_log (self, prefix, buf, len); } @@ -684,6 +686,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error) MMSerialPortPrivate *priv; char *devfile; const char *device; + struct serial_struct sinfo; g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE); @@ -696,11 +699,15 @@ mm_serial_port_open (MMSerialPort *self, GError **error) goto success; } - g_message ("(%s) opening serial device...", device); - devfile = g_strdup_printf ("/dev/%s", device); - errno = 0; - priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); - g_free (devfile); + mm_info ("(%s) opening serial port...", device); + + /* Only open a new file descriptor if we weren't given one already */ + if (priv->fd < 0) { + devfile = g_strdup_printf ("/dev/%s", device); + errno = 0; + priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); + g_free (devfile); + } if (priv->fd < 0) { /* nozomi isn't ready yet when the port appears, and it'll return @@ -733,6 +740,15 @@ mm_serial_port_open (MMSerialPort *self, GError **error) if (!MM_SERIAL_PORT_GET_CLASS (self)->config_fd (self, priv->fd, error)) goto error; + /* Don't wait for pending data when closing the port; this can cause some + * stupid devices that don't respond to URBs on a particular port to hang + * for 30 seconds when probin fails. + */ + if (ioctl (priv->fd, TIOCGSERIAL, &sinfo) == 0) { + sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE; + ioctl (priv->fd, TIOCSSERIAL, &sinfo); + } + priv->channel = g_io_channel_unix_new (priv->fd); g_io_channel_set_encoding (priv->channel, NULL, NULL); priv->watch_id = g_io_add_watch (priv->channel, @@ -745,13 +761,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error) 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); - } + mm_dbg ("(%s) device open count is %d (open)", device, priv->open_count); return TRUE; error: @@ -785,13 +795,7 @@ mm_serial_port_close (MMSerialPort *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); - } + mm_dbg ("(%s) device open count is %d (close)", device, priv->open_count); if (priv->open_count > 0) return; @@ -802,7 +806,9 @@ mm_serial_port_close (MMSerialPort *self) } if (priv->fd >= 0) { - g_message ("(%s) closing serial device...", device); + GTimeVal tv_start, tv_end; + + mm_info ("(%s) closing serial port...", device); mm_port_set_connected (MM_PORT (self), FALSE); @@ -816,9 +822,24 @@ mm_serial_port_close (MMSerialPort *self) mm_serial_port_flash_cancel (self); + g_get_current_time (&tv_start); + tcsetattr (priv->fd, TCSANOW, &priv->old_t); + tcflush (priv->fd, TCIOFLUSH); close (priv->fd); priv->fd = -1; + + g_get_current_time (&tv_end); + + mm_info ("(%s) serial port closed", device); + + /* Some ports don't respond to data and when close is called + * the serial layer waits up to 30 second (closing_wait) for + * that data to send before giving up and returning from close(). + * Log that. See GNOME bug #630670 for more details. + */ + if (tv_end.tv_sec - tv_start.tv_sec > 20) + mm_warn ("(%s): close blocked by driver for more than 20 seconds!", device); } /* Clear the command queue */ @@ -1185,6 +1206,9 @@ set_property (GObject *object, guint prop_id, MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); switch (prop_id) { + case PROP_FD: + priv->fd = g_value_get_int (value); + break; case PROP_BAUD: priv->baud = g_value_get_uint (value); break; @@ -1213,6 +1237,9 @@ get_property (GObject *object, guint prop_id, MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object); switch (prop_id) { + case PROP_FD: + g_value_set_int (value, priv->fd); + break; case PROP_BAUD: g_value_set_uint (value, priv->baud); break; @@ -1275,6 +1302,14 @@ mm_serial_port_class_init (MMSerialPortClass *klass) klass->handle_response = real_handle_response; /* Properties */ + g_object_class_install_property + (object_class, PROP_FD, + g_param_spec_int (MM_SERIAL_PORT_FD, + "File descriptor", + "Fiel descriptor", + -1, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_BAUD, g_param_spec_uint (MM_SERIAL_PORT_BAUD, diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h index f78f793..5fee1b4 100644 --- a/src/mm-serial-port.h +++ b/src/mm-serial-port.h @@ -35,6 +35,7 @@ #define MM_SERIAL_PORT_PARITY "parity" #define MM_SERIAL_PORT_STOPBITS "stopbits" #define MM_SERIAL_PORT_SEND_DELAY "send-delay" +#define MM_SERIAL_PORT_FD "fd" /* Construct-only */ typedef struct _MMSerialPort MMSerialPort; typedef struct _MMSerialPortClass MMSerialPortClass; diff --git a/src/mm-utils.c b/src/mm-utils.c index 56182c0..c435d1d 100644 --- a/src/mm-utils.c +++ b/src/mm-utils.c @@ -76,3 +76,17 @@ utils_hexstr2bin (const char *hex, gsize *out_len) /* End from hostap */ +char * +utils_bin2hexstr (const guint8 *bin, gsize len) +{ + GString *ret; + gsize i; + + g_return_val_if_fail (bin != NULL, NULL); + + ret = g_string_sized_new (len * 2 + 1); + for (i = 0; i < len; i++) + g_string_append_printf (ret, "%.2X", bin[i]); + return g_string_free (ret, FALSE); +} + diff --git a/src/mm-utils.h b/src/mm-utils.h index 79e7827..1b9b328 100644 --- a/src/mm-utils.h +++ b/src/mm-utils.h @@ -20,5 +20,7 @@ int utils_hex2byte (const char *hex); char *utils_hexstr2bin (const char *hex, gsize *out_len); +char *utils_bin2hexstr (const guint8 *bin, gsize len); + #endif /* MM_UTILS_H */ diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 74255db..e265bc1 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -1,7 +1,10 @@ INCLUDES = \ -I$(top_srcdir)/src -noinst_PROGRAMS = test-modem-helpers +noinst_PROGRAMS = \ + test-modem-helpers \ + test-charsets \ + test-qcdm-serial-port test_modem_helpers_SOURCES = \ test-modem-helpers.c @@ -13,10 +16,36 @@ test_modem_helpers_LDADD = \ $(top_builddir)/src/libmodem-helpers.la \ $(MM_LIBS) +test_charsets_SOURCES = \ + test-charsets.c + +test_charsets_CPPFLAGS = \ + $(MM_CFLAGS) + +test_charsets_LDADD = \ + $(top_builddir)/src/libmodem-helpers.la \ + $(MM_LIBS) + +test_qcdm_serial_port_SOURCES = \ + test-qcdm-serial-port.c + +test_qcdm_serial_port_CPPFLAGS = \ + $(MM_CFLAGS) \ + -I$(top_srcdir) + +test_qcdm_serial_port_LDADD = \ + $(MM_LIBS) \ + $(top_builddir)/src/libserial.la \ + $(top_builddir)/src/libmodem-helpers.la \ + $(top_builddir)/libqcdm/src/libqcdm.la \ + -lutil + if WITH_TESTS check-local: test-modem-helpers $(abs_builddir)/test-modem-helpers + $(abs_builddir)/test-charsets + $(abs_builddir)/test-qcdm-serial-port endif diff --git a/src/tests/test-charsets.c b/src/tests/test-charsets.c new file mode 100644 index 0000000..656b80c --- /dev/null +++ b/src/tests/test-charsets.c @@ -0,0 +1,323 @@ +/* -*- 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 +#include + +#include "mm-modem-helpers.h" + +static void +test_def_chars (void *f, gpointer d) +{ + /* Test that a string with all the characters in the GSM 03.38 charset + * are converted from UTF-8 to GSM and back to UTF-8 successfully. + */ + static const char *s = "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà"; + guint8 *gsm, *utf8; + guint32 len = 0; + + /* Convert to GSM */ + gsm = mm_charset_utf8_to_unpacked_gsm (s, &len); + g_assert (gsm); + g_assert_cmpint (len, ==, 127); + + /* And back to UTF-8 */ + utf8 = mm_charset_gsm_unpacked_to_utf8 (gsm, len); + g_assert (utf8); + g_assert_cmpstr (s, ==, (const char *) utf8); + + g_free (gsm); + g_free (utf8); +} + +static void +test_esc_chars (void *f, gpointer d) +{ + /* Test that a string with all the characters in the extended GSM 03.38 + * charset are converted from UTF-8 to GSM and back to UTF-8 successfully. + */ + static const char *s = "\f^{}\\[~]|€"; + guint8 *gsm, *utf8; + guint32 len = 0; + + /* Convert to GSM */ + gsm = mm_charset_utf8_to_unpacked_gsm (s, &len); + g_assert (gsm); + g_assert_cmpint (len, ==, 20); + + /* And back to UTF-8 */ + utf8 = mm_charset_gsm_unpacked_to_utf8 (gsm, len); + g_assert (utf8); + g_assert_cmpstr (s, ==, (const char *) utf8); + + g_free (gsm); + g_free (utf8); +} + +static void +test_mixed_chars (void *f, gpointer d) +{ + /* Test that a string with a mix of GSM 03.38 default and extended characters + * is converted from UTF-8 to GSM and back to UTF-8 successfully. + */ + static const char *s = "@£$¥èéùìø\fΩΠΨΣΘ{ΞÆæß(})789\\:;<=>[?¡QRS]TUÖ|Ñܧ¿abpqrstuvöñüà€"; + guint8 *gsm, *utf8; + guint32 len = 0; + + /* Convert to GSM */ + gsm = mm_charset_utf8_to_unpacked_gsm (s, &len); + g_assert (gsm); + g_assert_cmpint (len, ==, 69); + + /* And back to UTF-8 */ + utf8 = mm_charset_gsm_unpacked_to_utf8 (gsm, len); + g_assert (utf8); + g_assert_cmpstr (s, ==, (const char *) utf8); + + g_free (gsm); + g_free (utf8); +} + +static void +test_unpack_gsm7 (void *f, gpointer d) +{ + static const guint8 gsm[] = { 0xC8, 0xF7, 0x1D, 0x14, 0x96, 0x97, 0x41, 0xF9, 0x77, 0xFD, 0x07 }; + static const guint8 expected[] = { 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f }; + guint8 *unpacked; + guint32 unpacked_len = 0; + + unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + g_assert (unpacked); + g_assert_cmpint (unpacked_len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); + + g_free (unpacked); +} + +static void +test_unpack_gsm7_7_chars (void *f, gpointer d) +{ + static const guint8 gsm[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 }; + static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x00 }; + guint8 *unpacked; + guint32 unpacked_len = 0; + + /* Tests the edge case where there are 7 bits left in the packed + * buffer but those 7 bits do not contain a character. In this case + * we expect a trailing NULL byte and the caller must know enough about + * the intended message to remove it when required. + */ + + unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + g_assert (unpacked); + g_assert_cmpint (unpacked_len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0); + + g_free (unpacked); +} + +static void +test_unpack_gsm7_all_chars (void *f, gpointer d) +{ + /* Packed array of all chars in GSM default and extended charset */ + static const guint8 gsm[] = { + 0x80, 0x80, 0x60, 0x40, 0x28, 0x18, 0x0E, 0x88, 0x84, 0x62, 0xC1, 0x68, + 0x38, 0x1E, 0x90, 0x88, 0x64, 0x42, 0xA9, 0x58, 0x2E, 0x98, 0x8C, 0x66, + 0xC3, 0xE9, 0x78, 0x3E, 0xA0, 0x90, 0x68, 0x44, 0x2A, 0x99, 0x4E, 0xA8, + 0x94, 0x6A, 0xC5, 0x6A, 0xB9, 0x5E, 0xB0, 0x98, 0x6C, 0x46, 0xAB, 0xD9, + 0x6E, 0xB8, 0x9C, 0x6E, 0xC7, 0xEB, 0xF9, 0x7E, 0xC0, 0xA0, 0x70, 0x48, + 0x2C, 0x1A, 0x8F, 0xC8, 0xA4, 0x72, 0xC9, 0x6C, 0x3A, 0x9F, 0xD0, 0xA8, + 0x74, 0x4A, 0xAD, 0x5A, 0xAF, 0xD8, 0xAC, 0x76, 0xCB, 0xED, 0x7A, 0xBF, + 0xE0, 0xB0, 0x78, 0x4C, 0x2E, 0x9B, 0xCF, 0xE8, 0xB4, 0x7A, 0xCD, 0x6E, + 0xBB, 0xDF, 0xF0, 0xB8, 0x7C, 0x4E, 0xAF, 0xDB, 0xEF, 0xF8, 0xBC, 0x7E, + 0xCF, 0xEF, 0xFB, 0xFF, 0x1B, 0xC5, 0x86, 0xB2, 0x41, 0x6D, 0x52, 0x9B, + 0xD7, 0x86, 0xB7, 0xE9, 0x6D, 0x7C, 0x1B, 0xE0, 0xA6, 0x0C + }; + static const guint8 ext[] = { + 0x1B, 0x0A, 0x1B, 0x14, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2F, 0x1B, 0x3C, + 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x40, 0x1B, 0x65 + }; + guint8 *unpacked; + guint32 unpacked_len = 0; + int i; + + unpacked = gsm_unpack (gsm, sizeof (gsm), 0, &unpacked_len); + g_assert (unpacked); + g_assert_cmpint (unpacked_len, ==, 148); + + /* Test default chars */ + for (i = 0; i < 128; i++) + g_assert_cmpint (unpacked[i], ==, i); + + /* Text extended chars */ + g_assert_cmpint (memcmp ((guint8 *) (unpacked + 128), &ext[0], sizeof (ext)), ==, 0); + + g_free (unpacked); +} + +static void +test_pack_gsm7 (void *f, gpointer d) +{ + static const guint8 unpacked[] = { 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f }; + static const guint8 expected[] = { 0xC8, 0xF7, 0x1D, 0x14, 0x96, 0x97, 0x41, 0xF9, 0x77, 0xFD, 0x07 }; + guint8 *packed; + guint32 packed_len = 0; + + packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); + g_assert (packed); + g_assert_cmpint (packed_len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); + + g_free (packed); +} + +static void +test_pack_gsm7_7_chars (void *f, gpointer d) +{ + static const guint8 unpacked[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75 }; + static const guint8 expected[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 }; + guint8 *packed; + guint32 packed_len = 0; + + /* Tests the edge case where there are 7 bits left in the packed + * buffer but those 7 bits do not contain a character. In this case + * we expect a trailing NULL byte and the caller must know enough about + * the intended message to remove it when required. + */ + + packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); + g_assert (packed); + g_assert_cmpint (packed_len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); + + g_free (packed); +} + +#if 0 +static void +print_array (const guint8 *array, guint32 len) +{ + int col; + guint8 c; + + g_print ("\n"); + for (c = 0, col = 0; c < len; c++) { + g_print ("0x%02X, ", array[c] & 0xFF); + if (col++ == 11) { + col = 0; + g_print ("\n"); + } + } + g_print ("\n"); +} +#endif + +static void +test_pack_gsm7_all_chars (void *f, gpointer d) +{ + /* Packed array of all chars in GSM default and extended charset */ + static const guint8 expected[] = { + 0x80, 0x80, 0x60, 0x40, 0x28, 0x18, 0x0E, 0x88, 0x84, 0x62, 0xC1, 0x68, + 0x38, 0x1E, 0x90, 0x88, 0x64, 0x42, 0xA9, 0x58, 0x2E, 0x98, 0x8C, 0x66, + 0xC3, 0xE9, 0x78, 0x3E, 0xA0, 0x90, 0x68, 0x44, 0x2A, 0x99, 0x4E, 0xA8, + 0x94, 0x6A, 0xC5, 0x6A, 0xB9, 0x5E, 0xB0, 0x98, 0x6C, 0x46, 0xAB, 0xD9, + 0x6E, 0xB8, 0x9C, 0x6E, 0xC7, 0xEB, 0xF9, 0x7E, 0xC0, 0xA0, 0x70, 0x48, + 0x2C, 0x1A, 0x8F, 0xC8, 0xA4, 0x72, 0xC9, 0x6C, 0x3A, 0x9F, 0xD0, 0xA8, + 0x74, 0x4A, 0xAD, 0x5A, 0xAF, 0xD8, 0xAC, 0x76, 0xCB, 0xED, 0x7A, 0xBF, + 0xE0, 0xB0, 0x78, 0x4C, 0x2E, 0x9B, 0xCF, 0xE8, 0xB4, 0x7A, 0xCD, 0x6E, + 0xBB, 0xDF, 0xF0, 0xB8, 0x7C, 0x4E, 0xAF, 0xDB, 0xEF, 0xF8, 0xBC, 0x7E, + 0xCF, 0xEF, 0xFB, 0xFF, 0x1B, 0xC5, 0x86, 0xB2, 0x41, 0x6D, 0x52, 0x9B, + 0xD7, 0x86, 0xB7, 0xE9, 0x6D, 0x7C, 0x1B, 0xE0, 0xA6, 0x0C + }; + static const guint8 ext[] = { + 0x1B, 0x0A, 0x1B, 0x14, 0x1B, 0x28, 0x1B, 0x29, 0x1B, 0x2F, 0x1B, 0x3C, + 0x1B, 0x3D, 0x1B, 0x3E, 0x1B, 0x40, 0x1B, 0x65 + }; + guint8 *packed, c; + guint32 packed_len = 0; + GByteArray *unpacked; + + unpacked = g_byte_array_sized_new (148); + for (c = 0; c < 128; c++) + g_byte_array_append (unpacked, &c, 1); + for (c = 0; c < sizeof (ext); c++) + g_byte_array_append (unpacked, &ext[c], 1); + + packed = gsm_pack (unpacked->data, unpacked->len, 0, &packed_len); + g_assert (packed); + g_assert_cmpint (packed_len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0); + + g_free (packed); + g_byte_array_free (unpacked, TRUE); +} + +static void +test_pack_gsm7_24_chars (void *f, gpointer d) +{ + static const guint8 unpacked[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 + }; + guint8 *packed; + guint32 packed_len = 0; + + /* Tests that no empty trailing byte is added when all the 7-bit characters + * are packed into an exact number of bytes. + */ + + packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len); + g_assert (packed); + g_assert_cmpint (packed_len, ==, 21); + + g_free (packed); +} + + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else +typedef void (*TCFunc)(void); +#endif + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) + +int main (int argc, char **argv) +{ + GTestSuite *suite; + gint result; + + g_test_init (&argc, &argv, NULL); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE (test_def_chars, NULL)); + g_test_suite_add (suite, TESTCASE (test_esc_chars, NULL)); + g_test_suite_add (suite, TESTCASE (test_mixed_chars, NULL)); + + g_test_suite_add (suite, TESTCASE (test_unpack_gsm7, NULL)); + g_test_suite_add (suite, TESTCASE (test_unpack_gsm7_7_chars, NULL)); + g_test_suite_add (suite, TESTCASE (test_unpack_gsm7_all_chars, NULL)); + + g_test_suite_add (suite, TESTCASE (test_pack_gsm7, NULL)); + g_test_suite_add (suite, TESTCASE (test_pack_gsm7_7_chars, NULL)); + g_test_suite_add (suite, TESTCASE (test_pack_gsm7_all_chars, NULL)); + g_test_suite_add (suite, TESTCASE (test_pack_gsm7_24_chars, NULL)); + + result = g_test_run (); + + return result; +} + diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 92a7af8..946916f 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -17,6 +17,7 @@ #include #include "mm-modem-helpers.h" +#include "mm-log.h" typedef struct { GPtrArray *solicited_creg; @@ -710,6 +711,27 @@ test_creg_cgreg_multi2_unsolicited (void *f, gpointer d) test_creg_match ("Multi CREG/CGREG #2", FALSE, reply, data, &result); } +static void +test_cgreg2_x220_unsolicited (void *f, gpointer d) +{ + TestData *data = (TestData *) d; + const char *reply = "\r\n+CGREG: 2,1, 81ED, 1A9CEB\r\n"; + const CregResult result = { 1, 0x81ED, 0x1A9CEB, -1, 4, TRUE}; + + /* Tests random spaces in response */ + test_creg_match ("Alcatel One-Touch X220D CGREG=2", FALSE, reply, data, &result); +} + +static void +test_creg2_s8500_wave_unsolicited (void *f, gpointer d) +{ + TestData *data = (TestData *) d; + const char *reply = "\r\n+CREG: 2,1,000B,2816, B, C2816\r\n"; + const CregResult result = { 1, 0x000B, 0x2816, 0, 7, FALSE}; + + test_creg_match ("Samsung Wave S8500 CREG=2", FALSE, reply, data, &result); +} + static void test_cscs_icon225_support_response (void *f, gpointer d) { @@ -771,6 +793,372 @@ test_cscs_blackberry_support_response (void *f, gpointer d) g_assert (charsets == MM_MODEM_CHARSET_IRA); } +typedef struct { + char *devid; + char *desc; + guint vid; + guint pid; + const char *ati; + const char *ati1; + const char *gsn; + const char *revision; + const char *model; + const char *manf; +} DevidItem; + +static DevidItem devids[] = { + { "36e7a8e78637fd380b2664507ea5de8fc317d05b", + "Huawei E1550", + 0x12d1, 0x1001, + "\nManufacturer: huawei\n" + "Model: E1550\n" + "Revision: 11.608.09.01.21\n" + "IMEI: 235012412595195\n" + "+GCAP: +CGSM,+FCLASS,+DS\n", + NULL, + "\n235012412595195\n", + "\n11.608.09.01.21\n", + "\nE1550\n", + "\nhuawei\n" + }, + { "33b0fc4a06af5448df656ce12925979acf1cb600", + "Huawei EC121", + 0x12d1, 0x1411, + "\nManufacturer: HUAWEI INCORPORATED\n" + "Model: EC121\n" + "Revision: 11.100.17.00.114\n" + "ESN: +GSN:12de4fa6\n" + "+CIS707-A, +MS, +ES, +DS, +FCLASS\n", + NULL, + "\n12de4fa6\n", + "\n11.100.17.00.114\n", + "\nEC121\n", + "\nHUAWEI INCORPORATED\n" + }, + { "d17f016a402354eaa1e24855f4308fafca9cadb1", + "Sierra USBConnect Mercury", + 0x1199, 0x6880, + "\nManufacturer: Sierra Wireless, Inc.\n" + "Model: C885\n" + "Revision: J1_0_1_26AP C:/WS/FW/J1_0_1_26AP/MSM7200A/SRC/AMSS 2009/01/30 07:58:06\n" + "IMEI: 987866969112306\n" + "IMEI SV: 6\n" + "FSN: D603478104511\n" + "3GPP Release 6\n" + "+GCAP: +CGSM,+DS,+ES\n", + NULL, + "\n987866969112306\n", + "\nJ1_0_1_26AP C:/WS/FW/J1_0_1_26AP/MSM7200A/SRC/AMSS 2009/01/30 07:58:06\n", + "\nC885\n", + "\nSierra Wireless, Inc.\n" + }, + { "345e9eaad7624393aca85cde9bd859edf462414c", + "ZTE MF627", + 0x19d2, 0x0031, + "\nManufacturer: ZTE INCORPORATED\n" + "Model: MF627\n" + "Revision: BD_3GHAP673A4V1.0.0B02\n" + "IMEI: 023589923858188\n" + "+GCAP: +CGSM,+FCLASS,+DS\n", + NULL, + "\n023589923858188\n", + "\nBD_3GHAP673A4V1.0.0B02\n", + "\nMF627\n", + "\nZTE INCORPORATED\n" + }, + { "69fa133a668b6f4dbf39b73500fd153ec240c73f", + "Sony-Ericsson MD300", + 0x0fce, 0xd0cf, + "\nMD300\n", + "\nR3A018\n", + "\n349583712939483\n", + "\nR3A018\n", + "\nMD300\n", + "\nSony Ericsson\n" + }, + { "3dad89ed7d774938c38188cf29cf1c211e9d360b", + "Option iCON 7.2", + 0x0af0, 0x6901, + "\nManufacturer: Option N.V.\n" + "Model: GTM378\n" + "Revision: 2.5.21Hd (Date: Jun 17 2008, Time: 12:30:47)\n", + NULL, + "\n129512359199159,SE393939TS\n", + "\n2.5.21Hd (Date: Jun 17 2008, Time: 12:30:47)\n", + "\nGTM378\n", + "\nOption N.V.\n" + }, + { "b0acccb956c9eaf2076e03697e74bf998dc44179", + "ZTE MF622", + 0x19d2, 0x0001, + NULL, + NULL, + "\n235251122555115\n", + "\n3UKP671M3V1.0.0B08 3UKP671M3V1.0.0B08 1 [Jan 07 2008 16:00:00]\n", + "\nMF622\n", + "\nZTE INCORPORATED\n" + }, + { "29a5b258f1dc6f50c66a1a9a1ecdde97560799ab", + "Option 452", + 0x0af0, 0x7901, + "\nManufacturer: Option N.V.\n" + "Model: GlobeTrotter HSUPA Modem\n" + "Revision: 2.12.0.0Hd (Date: Oct 29 2009, Time: 09:56:48)\n", + "\nManufacturer: Option N.V.\n" + "Model: GlobeTrotter HSUPA Modem\n" + "Revision: 2.12.0.0Hd (Date: Oct 29 2009, Time: 09:56:48)\n", + "\n000125491259519,PH2155R3TR\n", + "\n2.12.0.0Hd (Date: Oct 29 2009, Time: 09:56:48)\n", + "\nGlobeTrotter HSUPA Modem\n", + "\nOption N.V.\n" + }, + { "c756c67e960e693d5d221e381ea170b60bb9288f", + "Novatel XU870", + 0x413c, 0x8118, + "\nManufacturer: Novatel Wireless Incorporated\n" + "Model: DELL XU870 ExpressCard\n" + "Revision: 9.5.05.01-02 [2006-10-20 17:19:09]\n" + "IMEI: 012051505051501\n" + "+GCAP: +CGSM,+DS\n", + "\nManufacturer: Novatel Wireless Incorporated\n" + "Model: DELL XU870 ExpressCard\n" + "Revision: 9.5.05.01-02 [2006-10-20 17:19:09]\n" + "IMEI: 012051505051501\n" + "+GCAP: +CGSM,+DS\n", + "\n012051505051501\n", + "\n9.5.05.01-02 [2006-10-20 17:19:09]\n", + "\nDELL XU870 ExpressCard\n", + "\nNovatel Wireless Incorporated\n" + }, + { "4162ba918ab54b7776bccc3830e6c6b7a6738244", + "Zoom 4596", + 0x1c9e, 0x9603, + "\nManufacturer: Manufacturer\n" + "Model: HSPA USB MODEM\n" + "Revision: LQA0021.1.1_M573A\n" + "IMEI: 239664699635121\n" + "+GCAP: +CGSM,+FCLASS,+DS\n", + "\nManufacturer: Manufacturer\n" + "Model: HSPA USB MODEM\n" + "Revision: LQA0021.1.1_M573A\n" + "IMEI: 239664699635121\n" + "+GCAP: +CGSM,+FCLASS,+DS\n", + "\n239664699635121\n", + "\nLQA0021.1.1_M573A\n", + "\nHSPA USB MODEM\n", + "\nManufacturer\n" + }, + { "6d3a2fccd3588943a8962fd1e0d3ba752c706660", + "C-MOTECH CDX-650", + 0x16d8, 0x6512, + "\nManufacturer: C-MOTECH Co., Ltd.\r\r\n" + "Model: CDX-650 \r\r\n" + "Revision: CDX65UAC03\r\r\n" + "Esn: 3B0C4B98\r\r\n" + "+GCAP: +CIS707A, +MS, +ES, +DS, +FCLASS\r\n", + "\nManufacturer: C-MOTECH Co., Ltd.\r\r\n" + "Model: CDX-650 \r\r\n" + "Revision: CDX65UAC03\r\r\n" + "Esn: 3B0C4B98\r\r\n" + "+GCAP: +CIS707A, +MS, +ES, +DS, +FCLASS\r\n", + "\n0x3B0C4B98\n", + "\nCDX65UAC03 1 [Oct 17 2007 13:30:00]\n", + "\nModel CDX-650 \n", + "\nC-MOTECH Co., Ltd.\n" + }, + { "cf50da63e6d48beb1d1c3b41d70ef6fa534c3e13", + "BUSlink SCWi275u", + 0x22b8, 0x3802, + "\n144\n", + "\n000\n", + NULL, + "\n\"ADE_05_00_06032300I\"\n", + "\n\"GSM900\",\"GSM1800\",\"GSM1900\",\"GSM850\",\"MODEL=I250-000\"\n", + "\n\"Motorola CE, Copyright 2000\"\n" + }, + { "2aff568f2b60f3d6f3f6cac708ed5dce77b12b96", + "Motorola ROKR E2", + 0x22b8, 0x3802, + NULL, + NULL, + "\n\"626936926396996\"\n", + "\n\"R564_G_12.00.47P\"\n", + "\n\"E2\"\n", + "\n\"Motorola\"\n" + }, + { "a7136c6067a43f055ca093cee75cb98ce6c9658e", + "Sony-Ericsson W580i", + 0x0fce, 0xd089, + "\nSony Ericsson W580\n", + "\nCXC1123481\n", + "\n012505051512505\n", + "\nR8BE001 080115 1451 CXC1123481_NAM_1_LA\n", + "\nAAC-1052042-BV\n", + "\nSony Ericsson\n" + }, + { "b80ee70214bdf9672f2a268ce165ecfd9def5721", + "Huawei E226", + 0x12d1, 0x1003, + "\nManufacturer: huawei\n" + "Model: E226\n" + "Revision: 11.310.15.00.150\n" + "IMEI: 232363662362362\n" + "+GCAP: +CGSM,+FCLASS,+DS\n", + "\nManufacturer: huawei\n" + "Model: E226\n" + "Revision: 11.310.15.00.150\n" + "IMEI: 232363662362362\n" + "+GCAP: +CGSM,+FCLASS,+DS\n", + "\n232363662362362\n", + "\n11.310.15.00.150\n", + "\nE226\n", + "\nhuawei\n" + }, + { "d902e1f234863aa107bfc2d0faefbee5ed6901f1", + "LG LX265", + 0x1004, 0x6000, + "\nManufacturer: +GMI: LG Electronics Inc.\n" + "Model: +GMI: LG Electronics Inc.+GMM: Model:LG-LX265\n" + "Revision: +GMR: LX265V05, 50571\n" + "ESN: +GSN: 0x9235EB52\n" + "+GCAP: +CIS707-A, +MS, +ES, +DS, +FCLASS\n", + "\nManufacturer: +GMI: LG Electronics Inc.\n" + "Model: +GMI: LG Electronics Inc.+GMM: Model:LG-LX265\n" + "Revision: +GMR: LX265V05, 50571\n" + "ESN: +GSN: 0x9235EB52\n" + "+GCAP: +CIS707-A, +MS, +ES, +DS, +FCLASS\n", + "\n0x9235EB52\n", + "\nLX265V05, 50571\n", + "\nModel:LG-LX265\n", + "\nLG Electronics Inc.\n" + }, + { "543c2920e450e20a46368861fdec3a3b97ba8663", + "Nokia 2720a BT", + 0x0000, 0x0000, + "\nNokia\n", + "\n012350150101501\n", + "\n012350150101501\n", + "\nV 08.62\n" + "24-07-09\n" + "RM-520\n" + "(c) Nokia \n", + "\nNokia 2720a-2b\n", + "\nNokia\n" + }, + { "6386ffa7a39ced3c9bfd1d693b90975661e54a86", + "Gobi 1000", + 0x03f0, 0x1f1d, + "\nManufacturer: QUALCOMM INCORPORATED\n" + "Model: 88\n" + "Revision: D1020-SUUAASFA-4352 1 [Apr 14 2008 18:00:00]\n" + "IMEI: 239639269236269\n" + "+GCAP: +CGSM,+DS\n", + "\nManufacturer: QUALCOMM INCORPORATED\n" + "Model: 88\n" + "Revision: D1020-SUUAASFA-4352 1 [Apr 14 2008 18:00:00]\n" + "IMEI: 239639269236269\n" + "+GCAP: +CGSM,+DS\n", + "\n239639269236269\n", + "\nD1020-SUUAASFA-4352 1 [Apr 14 2008 18:00:00]\n", + "\n88\n", + "\nQUALCOMM INCORPORATED\n" + }, + { NULL } +}; + +static void +test_devid_item (void *f, gpointer d) +{ + DevidItem *item = (DevidItem *) d; + char *devid; + + g_print ("%s... ", item->desc); + devid = mm_create_device_identifier (item->vid, + item->pid, + item->ati, + item->ati1, + item->gsn, + item->revision, + item->model, + item->manf); + g_assert (devid); + if (strcmp (devid, item->devid)) + g_message ("%s", devid); + g_assert (!strcmp (devid, item->devid)); +} + +typedef struct { + const char *desc; + const gint min; + const gint max; +} CindEntry; + +static void +test_cind_results (const char *desc, + const char *reply, + CindEntry *expected_results, + guint32 expected_results_len) +{ + guint i; + GError *error = NULL; + GHashTable *results; + + g_print ("\nTesting %s +CIND response...\n", desc); + + results = mm_parse_cind_test_response (reply, &error); + g_assert (results); + g_assert (error == NULL); + + g_assert (g_hash_table_size (results) == expected_results_len); + + for (i = 0; i < expected_results_len; i++) { + CindEntry *expected = &expected_results[i]; + CindResponse *compare; + + compare = g_hash_table_lookup (results, expected->desc); + g_assert (compare); + g_assert_cmpint (i + 1, ==, cind_response_get_index (compare)); + g_assert_cmpint (expected->min, ==, cind_response_get_min (compare)); + g_assert_cmpint (expected->max, ==, cind_response_get_max (compare)); + } + + g_hash_table_destroy (results); +} + +static void +test_cind_response_linktop_lw273 (void *f, gpointer d) +{ + const char *reply = "+CIND: (\"battchg\",(0-5)),(\"signal\",(0-5)),(\"batterywarning\",(0-1)),(\"chargerconnected\",(0-1)),(\"service\",(0-1)),(\"sounder\",(0-1)),(\"message\",(0-1)),()"; + static CindEntry expected[] = { + { "battchg", 0, 5 }, + { "signal", 0, 5 }, + { "batterywarning", 0, 1 }, + { "chargerconnected", 0, 1 }, + { "service", 0, 1 }, + { "sounder", 0, 1 }, + { "message", 0, 1 } + }; + + test_cind_results ("LW273", reply, &expected[0], ARRAY_LEN (expected)); +} + +static void +test_cind_response_moto_v3m (void *f, gpointer d) +{ + const char *reply = "+CIND: (\"Voice Mail\",(0,1)),(\"service\",(0,1)),(\"call\",(0,1)),(\"Roam\",(0-2)),(\"signal\",(0-5)),(\"callsetup\",(0-3)),(\"smsfull\",(0,1))"; + static CindEntry expected[] = { + { "voicemail", 0, 1 }, + { "service", 0, 1 }, + { "call", 0, 1 }, + { "roam", 0, 2 }, + { "signal", 0, 5 }, + { "callsetup", 0, 3 }, + { "smsfull", 0, 1 } + }; + + test_cind_results ("Motorola V3m", reply, &expected[0], ARRAY_LEN (expected)); +} + static TestData * test_data_new (void) { @@ -790,8 +1178,21 @@ test_data_free (TestData *data) g_free (data); } +void +_mm_log (const char *loc, + const char *func, + guint32 level, + const char *fmt, + ...) +{ + /* Dummy log function */ +} +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else typedef void (*TCFunc)(void); +#endif #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) @@ -800,6 +1201,7 @@ int main (int argc, char **argv) GTestSuite *suite; TestData *data; gint result; + DevidItem *item = &devids[0]; g_test_init (&argc, &argv, NULL); @@ -848,12 +1250,14 @@ int main (int argc, char **argv) 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_creg2_s8500_wave_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_cgreg2_x220_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)); @@ -863,6 +1267,14 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_cscs_buslink_support_response, data)); g_test_suite_add (suite, TESTCASE (test_cscs_blackberry_support_response, data)); + g_test_suite_add (suite, TESTCASE (test_cind_response_linktop_lw273, data)); + g_test_suite_add (suite, TESTCASE (test_cind_response_moto_v3m, data)); + + while (item->devid) { + g_test_suite_add (suite, TESTCASE (test_devid_item, (gconstpointer) item)); + item++; + } + result = g_test_run (); test_data_free (data); diff --git a/src/tests/test-qcdm-serial-port.c b/src/tests/test-qcdm-serial-port.c new file mode 100644 index 0000000..3aeed6a --- /dev/null +++ b/src/tests/test-qcdm-serial-port.c @@ -0,0 +1,482 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mm-errors.h" +#include "mm-qcdm-serial-port.h" +#include "libqcdm/src/commands.h" +#include "libqcdm/src/utils.h" +#include "libqcdm/src/com.h" +#include "mm-log.h" + +typedef struct { + int master; + int slave; + gboolean valid; + pid_t child; +} TestData; + +static gboolean +wait_for_child (TestData *d, guint32 timeout) +{ + GTimeVal start, now; + int status, ret; + + g_get_current_time (&start); + do { + status = 0; + ret = waitpid (d->child, &status, WNOHANG); + g_get_current_time (&now); + if (d->child && (now.tv_sec - start.tv_sec > timeout)) { + /* Kill it */ + if (g_test_verbose ()) + g_message ("Killing running child process %d", d->child); + kill (d->child, SIGKILL); + d->child = 0; + } + if (ret == 0) + sleep (1); + } while ((ret <= 0) || (!WIFEXITED (status) && !WIFSIGNALED (status))); + + d->child = 0; + return (WIFEXITED (status) && WEXITSTATUS (status) == 0) ? TRUE : FALSE; +} + +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 void +server_send_response (int fd, const char *buf, gsize len) +{ + int status; + gsize i = 0; + + if (g_test_verbose ()) + print_buf (">>>", buf, len); + + while (i < len) { + errno = 0; + status = write (fd, &buf[i], 1); + g_assert_cmpint (errno, ==, 0); + g_assert (status == 1); + i++; + usleep (1000); + } +} + +static gsize +server_wait_request (int fd, 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 (fd, &in); + result = select (fd + 1, &in, NULL, NULL, &timeout); + g_assert (result == 1); + g_assert (FD_ISSET (fd, &in)); + + do { + errno = 0; + bytes_read = read (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 */ + g_assert_not_reached (); + } + } while (total < sizeof (readbuf)); + + if (g_test_verbose ()) { + print_buf ("<<<", readbuf, total); + print_buf ("D<<", buf, decap_len); + } + + return decap_len; +} + +typedef void (*VerInfoCb) (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data); + +static void +qcdm_verinfo_expect_success_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_assert_no_error (error); + g_assert (response->len > 0); + g_main_loop_quit (loop); +} + +static void +qcdm_request_verinfo (MMQcdmSerialPort *port, VerInfoCb cb, GMainLoop *loop) +{ + GError *error = NULL; + GByteArray *verinfo; + gint len; + + /* 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_assert_no_error (error); + } + verinfo->len = len; + + mm_qcdm_serial_port_queue_command (port, verinfo, 3, cb, loop); +} + +static void +qcdm_test_child (int fd, VerInfoCb cb) +{ + MMQcdmSerialPort *port; + GMainLoop *loop; + gboolean success; + GError *error = NULL; + + /* In the child */ + g_type_init (); + + loop = g_main_loop_new (NULL, FALSE); + + port = mm_qcdm_serial_port_new_fd (fd, MM_PORT_TYPE_PRIMARY); + g_assert (port); + + success = mm_serial_port_open (MM_SERIAL_PORT (port), &error); + g_assert_no_error (error); + g_assert (success); + + qcdm_request_verinfo (port, cb, loop); + g_main_loop_run (loop); + + mm_serial_port_close (MM_SERIAL_PORT (port)); + g_object_unref (port); +} + +/* Test that a Version Info request/response is processed correctly to + * make sure things in general are working. + */ +static void +test_verinfo (void *f) +{ + TestData *d = f; + char req[512]; + gsize req_len; + pid_t cpid; + const char rsp[] = { + 0x00, 0x41, 0x75, 0x67, 0x20, 0x31, 0x39, 0x20, 0x32, 0x30, 0x30, 0x38, + 0x32, 0x30, 0x3a, 0x34, 0x38, 0x3a, 0x34, 0x37, 0x4f, 0x63, 0x74, 0x20, + 0x32, 0x39, 0x20, 0x32, 0x30, 0x30, 0x37, 0x31, 0x39, 0x3a, 0x30, 0x30, + 0x3a, 0x30, 0x30, 0x53, 0x43, 0x4e, 0x52, 0x5a, 0x2e, 0x2e, 0x2e, 0x2a, + 0x06, 0x04, 0xb9, 0x0b, 0x02, 0x00, 0xb2, 0x19, 0xc4, 0x7e + }; + + signal (SIGCHLD, SIG_DFL); + cpid = fork (); + g_assert (cpid >= 0); + + if (cpid == 0) { + /* In the child */ + qcdm_test_child (d->slave, qcdm_verinfo_expect_success_cb); + exit (0); + } + /* Parent */ + d->child = cpid; + + req_len = server_wait_request (d->master, req, sizeof (req)); + g_assert (req_len == 1); + g_assert_cmpint (req[0], ==, 0x00); + + server_send_response (d->master, rsp, sizeof (rsp)); + g_assert (wait_for_child (d, 3)); +} + +static void +qcdm_verinfo_expect_fail_cb (MMQcdmSerialPort *port, + GByteArray *response, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_assert_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL); + g_main_loop_quit (loop); +} + +/* Test that a Sierra CnS response to a Version Info command correctly + * raises an error in the child's response handler. + */ +static void +test_sierra_cns_rejected (void *f) +{ + TestData *d = f; + char req[512]; + gsize req_len; + pid_t cpid; + const char rsp[] = { + 0x7e, 0x00, 0x0a, 0x6b, 0x6d, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e + }; + + signal (SIGCHLD, SIG_DFL); + cpid = fork (); + g_assert (cpid >= 0); + + if (cpid == 0) { + /* In the child */ + qcdm_test_child (d->slave, qcdm_verinfo_expect_fail_cb); + exit (0); + } + /* Parent */ + d->child = cpid; + + req_len = server_wait_request (d->master, req, sizeof (req)); + g_assert (req_len == 1); + g_assert_cmpint (req[0], ==, 0x00); + + server_send_response (d->master, rsp, sizeof (rsp)); + + /* We expect the child to exit normally */ + g_assert (wait_for_child (d, 3)); +} + +/* Test that a random response to a Version Info command correctly + * raises an error in the child's response handler. + */ +static void +test_random_data_rejected (void *f) +{ + TestData *d = f; + char req[512]; + gsize req_len; + pid_t cpid; + const char rsp[] = { + 0x7e, 0x7e, 0x7e, 0x6b, 0x6d, 0x7e, 0x7e, 0x7e, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e + }; + + signal (SIGCHLD, SIG_DFL); + cpid = fork (); + g_assert (cpid >= 0); + + if (cpid == 0) { + /* In the child */ + qcdm_test_child (d->slave, qcdm_verinfo_expect_fail_cb); + exit (0); + } + /* Parent */ + d->child = cpid; + + req_len = server_wait_request (d->master, req, sizeof (req)); + g_assert (req_len == 1); + g_assert_cmpint (req[0], ==, 0x00); + + server_send_response (d->master, rsp, sizeof (rsp)); + + /* We expect the child to exit normally */ + g_assert (wait_for_child (d, 3)); +} + +/* Test that a bunch of frame markers at the beginning of a valid response + * to a Version Info command is parsed correctly. + */ +static void +test_leading_frame_markers (void *f) +{ + TestData *d = f; + char req[512]; + gsize req_len; + pid_t cpid; + const char rsp[] = { + 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, + 0x00, 0x41, 0x75, 0x67, 0x20, 0x31, 0x39, 0x20, 0x32, 0x30, 0x30, 0x38, + 0x32, 0x30, 0x3a, 0x34, 0x38, 0x3a, 0x34, 0x37, 0x4f, 0x63, 0x74, 0x20, + 0x32, 0x39, 0x20, 0x32, 0x30, 0x30, 0x37, 0x31, 0x39, 0x3a, 0x30, 0x30, + 0x3a, 0x30, 0x30, 0x53, 0x43, 0x4e, 0x52, 0x5a, 0x2e, 0x2e, 0x2e, 0x2a, + 0x06, 0x04, 0xb9, 0x0b, 0x02, 0x00, 0xb2, 0x19, 0xc4, 0x7e + }; + + signal (SIGCHLD, SIG_DFL); + cpid = fork (); + g_assert (cpid >= 0); + + if (cpid == 0) { + /* In the child */ + qcdm_test_child (d->slave, qcdm_verinfo_expect_success_cb); + exit (0); + } + /* Parent */ + d->child = cpid; + + req_len = server_wait_request (d->master, req, sizeof (req)); + g_assert (req_len == 1); + g_assert_cmpint (req[0], ==, 0x00); + + server_send_response (d->master, rsp, sizeof (rsp)); + + /* We expect the child to exit normally */ + g_assert (wait_for_child (d, 3)); +} + +static void +test_pty_create (gpointer user_data) +{ + TestData *d = user_data; + struct termios stbuf; + int ret; + GError *error = NULL; + gboolean success; + + ret = openpty (&d->master, &d->slave, NULL, NULL, NULL); + g_assert (ret == 0); + d->valid = TRUE; + + /* set raw mode on the slave using kernel default parameters */ + memset (&stbuf, 0, sizeof (stbuf)); + tcgetattr (d->slave, &stbuf); + tcflush (d->slave, TCIOFLUSH); + cfmakeraw (&stbuf); + tcsetattr (d->slave, TCSANOW, &stbuf); + fcntl (d->slave, F_SETFL, O_NONBLOCK); + + fcntl (d->master, F_SETFL, O_NONBLOCK); + success = qcdm_port_setup (d->master, &error); + g_assert_no_error (error); + g_assert (success); +} + +static void +test_pty_cleanup (gpointer user_data) +{ + TestData *d = user_data; + + /* For some reason the cleanup function gets called more times + * than the setup function does... + */ + if (d->valid) { + if (d->child) + kill (d->child, SIGKILL); + if (d->master >= 0) + close (d->master); + if (d->slave >= 0) + close (d->slave); + memset (d, 0, sizeof (*d)); + } +} + +#if GLIB_CHECK_VERSION(2,25,12) +typedef GTestFixtureFunc TCFunc; +#else +typedef void (*TCFunc)(void); +#endif + +#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL) +#define TESTCASE_PTY(t, d) g_test_create_case (#t, sizeof (*d), d, (TCFunc) test_pty_create, (TCFunc) t, (TCFunc) test_pty_cleanup) + +void +_mm_log (const char *loc, + const char *func, + guint32 level, + const char *fmt, + ...) +{ + /* Dummy log function */ +} + +int main (int argc, char **argv) +{ + GTestSuite *suite; + gint result; + TestData *data = NULL; + + g_test_init (&argc, &argv, NULL); + + suite = g_test_get_root (); + + g_test_suite_add (suite, TESTCASE_PTY (test_verinfo, data)); + g_test_suite_add (suite, TESTCASE_PTY (test_sierra_cns_rejected, data)); + g_test_suite_add (suite, TESTCASE_PTY (test_random_data_rejected, data)); + g_test_suite_add (suite, TESTCASE_PTY (test_leading_frame_markers, data)); + + result = g_test_run (); + + return result; +} + diff --git a/test/Makefile.am b/test/Makefile.am index 3d571c3..2fbc519 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,5 +19,11 @@ lsudev_LDADD = $(GUDEV_LIBS) EXTRA_DIST = \ - mm-test.py + mm-test.py \ + disable.py \ + enable.py \ + info.py \ + list-modems.py \ + location.py \ + mm-send-sms.py diff --git a/test/disable.py b/test/disable.py new file mode 100755 index 0000000..5b8c14a --- /dev/null +++ b/test/disable.py @@ -0,0 +1,27 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2009 - 2010 Red Hat, Inc. +# + +import sys, dbus + +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' + +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) +modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM) +modem.Enable (False) + diff --git a/test/enable.py b/test/enable.py new file mode 100755 index 0000000..bfe1a6d --- /dev/null +++ b/test/enable.py @@ -0,0 +1,27 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2009 - 2010 Red Hat, Inc. +# + +import sys, dbus + +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' + +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) +modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM) +modem.Enable (True) + diff --git a/test/info.py b/test/info.py new file mode 100755 index 0000000..6659a3a --- /dev/null +++ b/test/info.py @@ -0,0 +1,256 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2008 Novell, Inc. +# Copyright (C) 2009 Red Hat, Inc. +# + +import sys, dbus + +DBUS_INTERFACE_PROPERTIES='org.freedesktop.DBus.Properties' +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +MM_DBUS_INTERFACE='org.freedesktop.ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' +MM_DBUS_INTERFACE_MODEM_CDMA='org.freedesktop.ModemManager.Modem.Cdma' +MM_DBUS_INTERFACE_MODEM_GSM_CARD='org.freedesktop.ModemManager.Modem.Gsm.Card' +MM_DBUS_INTERFACE_MODEM_GSM_NETWORK='org.freedesktop.ModemManager.Modem.Gsm.Network' + +def get_cdma_band_class(band_class): + if band_class == 1: + return "800MHz" + elif band_class == 2: + return "1900MHz" + else: + return "Unknown" + +def get_reg_state(state): + if state == 1: + return "registered (roaming unknown)" + elif state == 2: + return "registered on home network" + elif state == 3: + return "registered on roaming network" + else: + return "unknown" + +def cdma_inspect(proxy, props): + cdma = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_CDMA) + + esn = cdma.GetEsn() + print "ESN: %s" % esn + + try: + (cdma_1x_state, evdo_state) = cdma.GetRegistrationState() + print "1x State: %s" % get_reg_state (cdma_1x_state) + print "EVDO State: %s" % get_reg_state (evdo_state) + except dbus.exceptions.DBusException, e: + print "Error reading registration state: %s" % e + + try: + quality = cdma.GetSignalQuality() + print "Signal quality: %d" % quality + except dbus.exceptions.DBusException, e: + print "Error reading signal quality: %s" % e + + try: + info = cdma.GetServingSystem() + print "Class: %s" % get_cdma_band_class(info[0]) + print "Band: %s" % info[1] + print "SID: %d" % info[2] + except dbus.exceptions.DBusException, e: + print "Error reading serving system: %s" % e + + +def get_gsm_network_mode(modem): + mode = modem.GetNetworkMode() + if mode == 0x0: + mode = "Unknown" + elif mode == 0x1: + mode = "Any" + elif mode == 0x2: + mode = "GPRS" + elif mode == 0x4: + mode = "EDGE" + elif mode == 0x8: + mode = "UMTS" + elif mode == 0x10: + mode = "HSDPA" + elif mode == 0x20: + mode = "2G Preferred" + elif mode == 0x40: + mode = "3G Preferred" + elif mode == 0x80: + mode = "2G Only" + elif mode == 0x100: + mode = "3G Only" + elif mode == 0x200: + mode = "HSUPA" + elif mode == 0x400: + mode = "HSPA" + else: + mode = "(Unknown)" + + print "Mode: %s" % mode + +def get_gsm_band(modem): + band = modem.GetBand() + if band == 0x0: + band = "Unknown" + elif band == 0x1: + band = "Any" + elif band == 0x2: + band = "EGSM (900 MHz)" + elif band == 0x4: + band = "DCS (1800 MHz)" + elif band == 0x8: + band = "PCS (1900 MHz)" + elif band == 0x10: + band = "G850 (850 MHz)" + elif band == 0x20: + band = "U2100 (WCSMA 2100 MHZ, Class I)" + elif band == 0x40: + band = "U1700 (WCDMA 3GPP UMTS1800 MHz, Class III)" + elif band == 0x80: + band = "17IV (WCDMA 3GPP AWS 1700/2100 MHz, Class IV)" + elif band == 0x100: + band = "U800 (WCDMA 3GPP UMTS800 MHz, Class VI)" + elif band == 0x200: + band = "U850 (WCDMA 3GPP UMT850 MHz, Class V)" + elif band == 0x400: + band = "U900 (WCDMA 3GPP UMTS900 MHz, Class VIII)" + elif band == 0x800: + band = "U17IX (WCDMA 3GPP UMTS MHz, Class IX)" + else: + band = "(invalid)" + + print "Band: %s" % band + + +mm_allowed = { 0: "any", + 1: "2G preferred", + 2: "3G preferred", + 3: "2G only", + 4: "3G only" + } + +mm_act = { 0: "unknown", + 1: "GSM", + 2: "GSM Compact", + 3: "GPRS", + 4: "EDGE", + 5: "UMTS", + 6: "HSDPA", + 7: "HSUPA", + 8: "HSPA" + } + +mm_reg = { 0: "idle", + 1: "home", + 2: "searching", + 3: "denied", + 4: "unknown", + 5: "roaming" + } + +def gsm_inspect(proxy, props): + # Gsm.Card interface + card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_CARD) + + simid = "" + try: + simid = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_CARD, "SimIdentifier") + except dbus.exceptions.DBusException: + pass + print "SIM ID: %s" % simid + + imei = "" + try: + imei = card.GetImei() + except dbus.exceptions.DBusException: + pass + print "IMEI: %s" % imei + + imsi = "" + try: + imsi = card.GetImsi() + except dbus.exceptions.DBusException: + pass + print "IMSI: %s" % imsi + + opid = "" + try: + opid = card.GetOperatorId() + except dbus.exceptions.DBusException: + pass + print "Operator ID: %s" % opid + + # Gsm.Network interface + net = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_NETWORK) + try: + quality = net.GetSignalQuality() + print "Signal quality: %d" % quality + except dbus.exceptions.DBusException, e: + print "Error reading signal quality: %s" % e + + try: + reg = net.GetRegistrationInfo() + print "Reg status: %s (%s, '%s')" % (mm_reg[int(reg[0])], reg[1], reg[2]) + except dbus.exceptions.DBusException, e: + print "Error reading registration: %s" % e + + try: + allowed = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_NETWORK, "AllowedMode") + print "Allowed mode: %s" % mm_allowed[allowed] + except dbus.exceptions.DBusException, e: + print "Error reading allowed mode: %s" % e + + try: + act = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_NETWORK, "AccessTechnology") + print "Access Tech: %s" % mm_act[act] + except dbus.exceptions.DBusException, e: + print "Error reading current access technology: %s" % e + + + +bus = dbus.SystemBus() +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) + +# Properties +props = dbus.Interface(proxy, dbus_interface='org.freedesktop.DBus.Properties') + +mtype = props.Get(MM_DBUS_INTERFACE_MODEM, 'Type') +if mtype == 1: + print "Type: GSM" +elif mtype == 2: + print "Type: CDMA" + +print "Driver: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver')) +print "Modem device: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice')) +print "Data device: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Device')) +print "Device ID: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'DeviceIdentifier')) +print "" + +modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM) +info = modem.GetInfo() +print "Vendor: %s" % info[0] +print "Model: %s" % info[1] +print "Version: %s" % info[2] +print "" + +if mtype == 1: + gsm_inspect(proxy, props) +elif mtype == 2: + cdma_inspect(proxy, props) + diff --git a/test/list-modems.py b/test/list-modems.py new file mode 100755 index 0000000..c8cd618 --- /dev/null +++ b/test/list-modems.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2008 Novell, Inc. +# Copyright (C) 2009 - 2010 Red Hat, Inc. +# + +import sys, dbus + +DBUS_INTERFACE_PROPERTIES='org.freedesktop.DBus.Properties' +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +MM_DBUS_INTERFACE='org.freedesktop.ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' + +bus = dbus.SystemBus() + +# Get available modems: +manager_proxy = bus.get_object(MM_DBUS_SERVICE, MM_DBUS_PATH) +manager_iface = dbus.Interface(manager_proxy, dbus_interface=MM_DBUS_INTERFACE) +modems = manager_iface.EnumerateDevices() + +if not modems: + print "No modems found" + sys.exit(1) + +for m in modems: + proxy = bus.get_object(MM_DBUS_SERVICE, m) + + # Properties + props_iface = dbus.Interface(proxy, dbus_interface=DBUS_INTERFACE_PROPERTIES) + + driver = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Driver') + mtype = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'Type') + device = props_iface.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice') + + strtype = "" + if mtype == 1: + strtype = "GSM" + elif mtype == 2: + strtype = "CDMA" + + print "%s (%s [%s], device %s)" % (m, strtype, driver, device) + diff --git a/test/location.py b/test/location.py new file mode 100755 index 0000000..b6af387 --- /dev/null +++ b/test/location.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2009 - 2010 Red Hat, Inc. +# + +import sys, dbus, time + +DBUS_INTERFACE_PROPERTIES='org.freedesktop.DBus.Properties' +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_PATH='/org/freedesktop/ModemManager' +MM_DBUS_INTERFACE='org.freedesktop.ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' +MM_DBUS_INTERFACE_MODEM_LOCATION='org.freedesktop.ModemManager.Modem.Location' + +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_GPS_RAW = 0x00000004 + +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) +modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM) + +props = dbus.Interface(proxy, dbus_interface=DBUS_INTERFACE_PROPERTIES) +caps = props.Get(MM_DBUS_INTERFACE_MODEM_LOCATION, "Capabilities") + +print "Location Capabilities:" +if caps & MM_MODEM_LOCATION_CAPABILITY_GPS_NMEA: + print " GPS_NMEA" +if caps & MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI: + print " GSM_LAC_CI" +if caps & MM_MODEM_LOCATION_CAPABILITY_GPS_RAW: + print " GPS_RAW" +print "" + +loc = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_LOCATION) +loc.Enable(True, True) + +for i in range(0, 5): + locations = loc.GetLocation() + if locations.has_key(MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI): + print "GSM_LAC_CI: %s" % str(locations[MM_MODEM_LOCATION_CAPABILITY_GSM_LAC_CI]) + time.sleep(1) + +loc.Enable(False, False) + diff --git a/test/scan.py b/test/scan.py new file mode 100755 index 0000000..c892e65 --- /dev/null +++ b/test/scan.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2009 - 2010Red Hat, Inc. +# + +import sys, dbus + +DBUS_INTERFACE_PROPERTIES='org.freedesktop.DBus.Properties' +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_INTERFACE_MODEM='org.freedesktop.ModemManager.Modem' +MM_DBUS_INTERFACE_MODEM_GSM_NETWORK='org.freedesktop.ModemManager.Modem.Gsm.Network' + +mm_act = { 0: "unknown", + 1: "GSM", + 2: "GSM Compact", + 3: "GPRS", + 4: "EDGE", + 5: "UMTS", + 6: "HSDPA", + 7: "HSUPA", + 8: "HSPA" + } + +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) + +# Properties +props = dbus.Interface(proxy, dbus_interface='org.freedesktop.DBus.Properties') + +mtype = props.Get(MM_DBUS_INTERFACE_MODEM, 'Type') +if mtype == 2: + print "CDMA modems do not support network scans" + sys.exit(1) + +print "Driver: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver')) +print "Modem device: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice')) +print "Data device: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Device')) +print "" + +net = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_NETWORK) +print "Scanning..." +try: + results = net.Scan(timeout=120) +except dbus.exceptions.DBusException, e: + print "Error scanning: %s" % e + results = {} + +for r in results: + status = r['status'] + if status == "1": + status = "available" + elif status == "2": + status = "current" + elif status == "3": + status = "forbidden" + else: + status = "(Unknown)" + + access_tech = "" + try: + access_tech_num = int(r['access-tech']) + if access_tech_num == "0": + access_tech = "(GSM)" + elif access_tech_num == "1": + access_tech = "(Compact GSM)" + elif access_tech_num == "2": + access_tech = "(UMTS)" + elif access_tech_num == "3": + access_tech = "(EDGE)" + elif access_tech_num == "4": + access_tech = "(HSDPA)" + elif access_tech_num == "5": + access_tech = "(HSUPA)" + elif access_tech_num == "6": + access_tech = "(HSPA)" + except KeyError: + pass + + opnum = "(%s):" % r['operator-num'] + # Extra space for 5-digit MCC/MNC + if r['operator-num'] == 5: + opnum += " " + + if r.has_key('operator-long') and len(r['operator-long']): + print "%s %s %s %s" % (r['operator-long'], opnum, status, access_tech) + elif r.has_key('operator-short') and len(r['operator-short']): + print "%s %s %s %s" % (r['operator-short'], opnum, status, access_tech) + else: + print "%s: %s %s" % (r['operator-num'], status, access_tech) + diff --git a/test/ussd.py b/test/ussd.py new file mode 100755 index 0000000..2040f12 --- /dev/null +++ b/test/ussd.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details: +# +# Copyright (C) 2010 Guido Guenther +# +# Usage: ./test/ussd.py /org/freedesktop/ModemManager/Modems/0 '*130#' + +import sys, dbus, re + +MM_DBUS_SERVICE='org.freedesktop.ModemManager' +MM_DBUS_INTERFACE_USSD='org.freedesktop.ModemManager.Modem.Gsm.Ussd' + +bus = dbus.SystemBus() +proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1]) +modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_USSD) + +if len(sys.argv) != 3: + print "Usage: %s dbus_object ussd" + sys.exit(1) +else: + arg = sys.argv[2] + +# For testing purposes treat all "common" USSD sequences as initiate and the +# rest (except for cancel) as response. See GSM 02.90. +initiate_re = re.compile('[*#]{1,3}1[0-9][0-9].*#') + +if initiate_re.match(arg): + ret = modem.Initiate(arg) +elif arg == "cancel": + ret = modem.Cancel() +else: + ret = modem.Respond(arg) +print ret + -- cgit v1.2.3