Merge remote-tracking branch 'cros/upstream' into 'cros/master'
diff --git a/.gitignore b/.gitignore
index 2ce4de0..08366fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,3 @@
-INSTALL
-Makefile
-Makefile.in
-aclocal.m4
-autom4te.cache*
-compile
-config.*
-configure
-depcomp
-install-sh
-ltmain.sh
-intltool-*
-missing
-stamp-h1
-gtk-doc.make
*~
*.o
*.lo
@@ -20,153 +5,165 @@
*.loT
*.gcno
*.gcda
-libtool
-.deps
-.libs
-*-glue.h
-*.tar.bz2
+*.tar.xz
*.pyc
*.dirstamp
-org.freedesktop.ModemManager.service
-org.freedesktop.ModemManager.conf
-callouts/mm-modem-probe
-test/lsudev
+.deps
+.libs
+Makefile
+Makefile.in
-include/ModemManager-version.h
+/INSTALL
+/aclocal.m4
+/autom4te.cache*
+/compile
+/config.*
+/configure
+/depcomp
+/install-sh
+/ltmain.sh
+/intltool-*
+/missing
+/stamp-h1
+/gtk-doc.make
+/libtool
+/TAGS
+/ABOUT-NLS
-libmm-glib/generated/mm-gdbus-*.[ch]
-libmm-glib/generated/mm-enums-types.[ch]
-libmm-glib/generated/mm-errors-types.[ch]
-libmm-glib/generated/mm-errors-quarks.c
-libmm-glib/generated/*.xml
-libmm-glib/generated/tests/mm-gdbus-*.[ch]
-libmm-glib/tests/test-common-helpers
-libmm-glib/*.gir
-libmm-glib/*.typelib
+/include/ModemManager-version.h
+/include/ModemManager-names.h
-src/ModemManager
-src/mm-daemon-enums-types.c
-src/mm-daemon-enums-types.h
-src/mm-port-enums-types.c
-src/mm-port-enums-types.h
-src/mm-marshal.[ch]
-src/tests/test-modem-helpers
-src/tests/test-modem-helpers-qmi
-src/tests/test-charsets
-src/tests/test-qcdm-serial-port
-src/tests/test-at-serial-port
-src/tests/test-sms-part-3gpp
-src/tests/test-sms-part-cdma
+/libmm-glib/generated/mm-gdbus-*.[ch]
+/libmm-glib/generated/mm-enums-types.[ch]
+/libmm-glib/generated/mm-errors-types.[ch]
+/libmm-glib/generated/mm-errors-quarks.c
+/libmm-glib/generated/*.xml
+/libmm-glib/generated/tests/mm-gdbus-*.[ch]
+/libmm-glib/tests/test-common-helpers
+/libmm-glib/*.gir
+/libmm-glib/*.typelib
-cli/mmcli
+/src/ModemManager
+/src/mm-daemon-enums-types.c
+/src/mm-daemon-enums-types.h
+/src/mm-port-enums-types.c
+/src/mm-port-enums-types.h
+/src/mm-marshal.[ch]
+/src/tests/test-modem-helpers
+/src/tests/test-modem-helpers-qmi
+/src/tests/test-charsets
+/src/tests/test-qcdm-serial-port
+/src/tests/test-at-serial-port
+/src/tests/test-sms-part-3gpp
+/src/tests/test-sms-part-cdma
-vapi/libmm-glib.vapi
+/cli/mmcli
-libqcdm/tests/test-qcdm
-libqcdm/tests/modepref
-libqcdm/tests/ipv6pref
-libqcdm/tests/reset
+/vapi/libmm-glib.vapi
-libwmc/tests/test-wmc
+/libqcdm/tests/test-qcdm
+/libqcdm/tests/modepref
+/libqcdm/tests/ipv6pref
+/libqcdm/tests/reset
-data/org.freedesktop.ModemManager1.conf
-data/org.freedesktop.ModemManager1.service
-data/org.freedesktop.ModemManager1.policy
-data/org.freedesktop.ModemManager1.policy.in
-data/ModemManager.service
-data/ModemManager.pc
-data/mm-common.pc
-data/mm-glib.pc
-data/tests/org.freedesktop.ModemManager1.service
+/libwmc/tests/test-wmc
-include/ModemManager-names.h
+/data/org.freedesktop.ModemManager1.conf
+/data/org.freedesktop.ModemManager1.service
+/data/org.freedesktop.ModemManager1.policy
+/data/org.freedesktop.ModemManager1.policy.in
+/data/ModemManager.service
+/data/ModemManager.pc
+/data/mm-common.pc
+/data/mm-glib.pc
+/data/tests/org.freedesktop.ModemManager1.service
-po/Makefile.in.in
-po/POTFILES
-po/stamp-it
-po/.intltool-merge-cache
-po/ModemManager.pot
-po/Makevars.template
-po/Rules-quot
-po/boldquot.sed
-po/en@boldquot.header
-po/en@quot.header
-po/insert-header.sin
-po/quot.sed
-po/remove-potcdate.sin
-po/*.gmo
+/po/Makefile.in.in
+/po/POTFILES
+/po/stamp-it
+/po/.intltool-merge-cache
+/po/ModemManager.pot
+/po/Makevars.template
+/po/Rules-quot
+/po/boldquot.sed
+/po/en@boldquot.header
+/po/en@quot.header
+/po/insert-header.sin
+/po/quot.sed
+/po/remove-potcdate.sin
+/po/*.gmo
-docs/reference/api/version.xml
-docs/reference/api/ModemManager.args
-docs/reference/api/ModemManager.hierarchy
-docs/reference/api/ModemManager.interfaces
-docs/reference/api/ModemManager.prerequisites
-docs/reference/api/ModemManager.signals
-docs/reference/api/*.stamp
-docs/reference/api/*.txt
-docs/reference/api/html
-docs/reference/api/tmpl
-docs/reference/api/xml
-docs/reference/api/*.png
+/docs/reference/api/version.xml
+/docs/reference/api/ModemManager.args
+/docs/reference/api/ModemManager.hierarchy
+/docs/reference/api/ModemManager.interfaces
+/docs/reference/api/ModemManager.prerequisites
+/docs/reference/api/ModemManager.signals
+/docs/reference/api/*.stamp
+/docs/reference/api/*.txt
+/docs/reference/api/html
+/docs/reference/api/tmpl
+/docs/reference/api/xml
+/docs/reference/api/*.png
-docs/reference/libmm-glib/version.xml
-docs/reference/libmm-glib/libmm-glib.args
-docs/reference/libmm-glib/libmm-glib.hierarchy
-docs/reference/libmm-glib/libmm-glib.interfaces
-docs/reference/libmm-glib/libmm-glib.prerequisites
-docs/reference/libmm-glib/libmm-glib.signals
-docs/reference/libmm-glib/*.stamp
-docs/reference/libmm-glib/*.txt
-docs/reference/libmm-glib/html
-docs/reference/libmm-glib/tmpl
-docs/reference/libmm-glib/xml
+/docs/reference/libmm-glib/version.xml
+/docs/reference/libmm-glib/libmm-glib.args
+/docs/reference/libmm-glib/libmm-glib.hierarchy
+/docs/reference/libmm-glib/libmm-glib.interfaces
+/docs/reference/libmm-glib/libmm-glib.prerequisites
+/docs/reference/libmm-glib/libmm-glib.signals
+/docs/reference/libmm-glib/*.stamp
+/docs/reference/libmm-glib/*.txt
+/docs/reference/libmm-glib/html
+/docs/reference/libmm-glib/tmpl
+/docs/reference/libmm-glib/xml
-m4/gtk-doc.m4
-m4/intltool.m4
-m4/libtool.m4
-m4/lt*.m4
-m4/codeset.m4
-m4/gettext.m4
-m4/glibc2.m4
-m4/glibc21.m4
-m4/iconv.m4
-m4/intdiv0.m4
-m4/intl.m4
-m4/intldir.m4
-m4/intlmacosx.m4
-m4/intmax.m4
-m4/inttypes-pri.m4
-m4/inttypes_h.m4
-m4/lcmessage.m4
-m4/lib-ld.m4
-m4/lib-link.m4
-m4/lib-prefix.m4
-m4/lock.m4
-m4/longlong.m4
-m4/nls.m4
-m4/po.m4
-m4/printf-posix.m4
-m4/progtest.m4
-m4/size_max.m4
-m4/stdint_h.m4
-m4/uintmax_t.m4
-m4/visibility.m4
-m4/wchar_t.m4
-m4/wint_t.m4
-m4/xsize.m4
+/m4/gtk-doc.m4
+/m4/intltool.m4
+/m4/libtool.m4
+/m4/lt*.m4
+/m4/codeset.m4
+/m4/gettext.m4
+/m4/glibc2.m4
+/m4/glibc21.m4
+/m4/iconv.m4
+/m4/intdiv0.m4
+/m4/intl.m4
+/m4/intldir.m4
+/m4/intlmacosx.m4
+/m4/intmax.m4
+/m4/inttypes-pri.m4
+/m4/inttypes_h.m4
+/m4/lcmessage.m4
+/m4/lib-ld.m4
+/m4/lib-link.m4
+/m4/lib-prefix.m4
+/m4/lock.m4
+/m4/longlong.m4
+/m4/nls.m4
+/m4/po.m4
+/m4/printf-posix.m4
+/m4/progtest.m4
+/m4/size_max.m4
+/m4/stdint_h.m4
+/m4/uintmax_t.m4
+/m4/visibility.m4
+/m4/wchar_t.m4
+/m4/wint_t.m4
+/m4/xsize.m4
-uml290/uml290mode
+/uml290/uml290mode
-plugins/test-suite.log
-plugins/test-modem-helpers-huawei*
-plugins/test-modem-helpers-altair*
-plugins/test-modem-helpers-cinterion*
-plugins/test-modem-helpers-icera*
-plugins/test-modem-helpers-mbm*
-plugins/test-service-*
+/plugins/test-suite.log
+/plugins/test-modem-helpers-huawei*
+/plugins/test-modem-helpers-altair*
+/plugins/test-modem-helpers-cinterion*
+/plugins/test-modem-helpers-icera*
+/plugins/test-modem-helpers-mbm*
+/plugins/test-service-*
-TAGS
-ABOUT-NLS
+/test/lsudev
+/test/mmtty
-ModemManager-*-coverage.info
-ModemManager-*-coverage/
+/ModemManager-*-coverage.info
+/ModemManager-*-coverage/
diff --git a/configure.ac b/configure.ac
index 22417cc..816d5c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,9 @@
AC_PREREQ([2.63])
-dnl The MM version number
+dnl-----------------------------------------------------------------------------
+dnl Package and library versioning support
+dnl
+
m4_define([mm_major_version], [1])
m4_define([mm_minor_version], [5])
m4_define([mm_micro_version], [0])
@@ -19,12 +22,17 @@
m4_define([mm_glib_lt_revision], [0])
m4_define([mm_glib_lt_age], [2])
-
+dnl-----------------------------------------------------------------------------
+dnl autoconf, automake, libtool initialization
+dnl
AC_INIT([ModemManager],[mm_version],[modemmanager-devel@lists.freedesktop.org],[ModemManager])
-AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-xz -Wno-portability])
+AM_INIT_AUTOMAKE([1.11 subdir-objects tar-ustar no-dist-gzip dist-xz -Wno-portability])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
AM_MAINTAINER_MODE([enable])
+AM_SILENT_RULES([yes])
+
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS(config.h)
@@ -39,7 +47,11 @@
dnl Initialize libtool
LT_PREREQ([2.2])
-LT_INIT
+LT_INIT([disable-static])
+
+dnl-----------------------------------------------------------------------------
+dnl Version definitions
+dnl
dnl Version stuff
MM_MAJOR_VERSION=mm_major_version
@@ -59,14 +71,16 @@
AC_SUBST(MM_GLIB_LT_REVISION)
AC_SUBST(MM_GLIB_LT_AGE)
-dnl
+dnl-----------------------------------------------------------------------------
dnl Documentation
dnl
+
GTK_DOC_CHECK(1.0)
+dnl-----------------------------------------------------------------------------
+dnl i18n
dnl
-dnl translation support
-dnl
+
IT_PROG_INTLTOOL([0.40.0])
AM_GNU_GETTEXT([external])
@@ -76,6 +90,10 @@
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
+dnl-----------------------------------------------------------------------------
+dnl Build dependencies
+dnl
+
PKG_CHECK_MODULES(MM,
glib-2.0 >= 2.32
gmodule-2.0
@@ -104,27 +122,38 @@
AC_SUBST(GUDEV_CFLAGS)
AC_SUBST(GUDEV_LIBS)
-# Some required utilities
+dnl Some required utilities
GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
AC_SUBST(GLIB_MKENUMS)
+
GDBUS_CODEGEN=`$PKG_CONFIG --variable=gdbus_codegen gio-2.0`
AC_SUBST(GDBUS_CODEGEN)
-# Code coverage (disabled by default)
+dnl-----------------------------------------------------------------------------
+dnl Testing support
+dnl
+
+dnl Code coverage (disabled by default)
AX_CODE_COVERAGE
-# GObject Introspection
+dnl-----------------------------------------------------------------------------
+dnl Introspection and bindings
+dnl
+
+dnl GObject Introspection
GOBJECT_INTROSPECTION_CHECK([0.9.6])
-# Vala bindings
+dnl Vala bindings
VAPIGEN_CHECK(0.18)
-
-# Sanity check
if test "x$enable_vala" = "xyes" -a ! -f "$VAPIGEN_MAKEFILE"; then
AC_MSG_ERROR([Vala bindings enabled but Makefile.vapigen not found. Install vala-devel, or pass --disable-vala])
fi
-# DBus system directory
+dnl-----------------------------------------------------------------------------
+dnl System paths
+dnl
+
+dnl DBus system directory
AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is]))
if test -n "$with_dbus_sys_dir" ; then
DBUS_SYS_DIR="$with_dbus_sys_dir"
@@ -133,7 +162,7 @@
fi
AC_SUBST(DBUS_SYS_DIR)
-# udev base directory
+dnl udev base directory
AC_ARG_WITH(udev-base-dir, AS_HELP_STRING([--with-udev-base-dir=DIR], [where udev base directory is]))
if test -n "$with_udev_base_dir" ; then
UDEV_BASE_DIR="$with_udev_base_dir"
@@ -142,7 +171,7 @@
fi
AC_SUBST(UDEV_BASE_DIR)
-# systemd system unit directory
+dnl systemd system unit directory
AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [where systemd service files are]),
[], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
if test "x$with_systemdsystemunitdir" != xno; then
@@ -150,14 +179,46 @@
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$SYSTEMD_UNIT_DIR" -a "$SYSTEMD_UNIT_DIR" != xno ])
-# PolicyKit
+dnl-----------------------------------------------------------------------------
+dnl Suspend/resume support
+dnl
+
+AC_ARG_WITH(suspend-resume, AS_HELP_STRING([--with-suspend-resume=no|upower|systemd], [Build ModemManager with specific suspend/resume support]))
+
+if test "x$with_suspend_resume" = "x"; then
+ with_suspend_resume="none"
+fi
+
+case $with_suspend_resume in
+ none)
+ AC_DEFINE(WITH_SUSPEND_RESUME, 0, [Define if you have suspend-resume support])
+ ;;
+ upower)
+ AC_DEFINE(WITH_SUSPEND_RESUME, 1, [Define if you have suspend-resume support])
+ ;;
+ systemd)
+ PKG_CHECK_MODULES(SYSTEMD_INHIBIT, [libsystemd >= 209],,
+ [PKG_CHECK_MODULES(SYSTEMD_INHIBIT, [libsystemd-login >= 183])])
+ AC_DEFINE(WITH_SUSPEND_RESUME, 1, [Define if you have suspend-resume support])
+ ;;
+ *)
+ AC_MSG_ERROR(--with-suspend-resume must be one of [none, upower, systemd])
+ ;;
+esac
+
+AM_CONDITIONAL(SUSPEND_RESUME_UPOWER, test "x$with_suspend_resume" = "xupower")
+AM_CONDITIONAL(SUSPEND_RESUME_SYSTEMD, test "x$with_suspend_resume" = "xsystemd")
+
+dnl-----------------------------------------------------------------------------
+dnl PolicyKit
+dnl
+
PKG_CHECK_MODULES(POLKIT, [polkit-gobject-1 >= 0.97], [have_polkit=yes],[have_polkit=no])
AC_ARG_WITH(polkit,
AS_HELP_STRING([--with-polkit=(strict|permissive|none)],
[Enable PolicyKit support [[default=auto]]]),,
[with_polkit=auto])
-# Handle 'auto' ('strict' if polkit found, 'none' otherwise),
-# 'yes' ('strict') and 'no' ('none')
+
if test "x$with_polkit" = "xauto"; then
if test "x$have_polkit" = "xno"; then
with_polkit="none"
@@ -169,7 +230,7 @@
elif test "x$with_polkit" = "xyes"; then
with_polkit=strict
fi
-# Build policies context
+
if test "x$with_polkit" = "xnone"; then
AC_DEFINE(WITH_POLKIT, 0, [Define if you have PolicyKit support])
else
@@ -197,9 +258,10 @@
AM_CONDITIONAL(WITH_POLKIT, [test "x$with_polkit" != "xnone" ])
-dnl
+dnl-----------------------------------------------------------------------------
dnl MBIM support (enabled by default)
dnl
+
AC_ARG_WITH(mbim, AS_HELP_STRING([--without-mbim], [Build without MBIM support]), [], [with_mbim=yes])
AM_CONDITIONAL(WITH_MBIM, test "x$with_mbim" = "xyes")
case $with_mbim in
@@ -218,14 +280,14 @@
;;
esac
-dnl
+dnl-----------------------------------------------------------------------------
dnl QMI support (enabled by default)
dnl
AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes])
AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes")
case $with_qmi in
yes)
- PKG_CHECK_MODULES(QMI, [qmi-glib >= 1.11.1], [have_qmi=yes],[have_qmi=no])
+ PKG_CHECK_MODULES(QMI, [qmi-glib >= 1.13.4], [have_qmi=yes],[have_qmi=no])
if test "x$have_qmi" = "xno"; then
AC_MSG_ERROR([Couldn't find libqmi-glib. Install it, or otherwise configure using --without-qmi to disable QMI support.])
else
@@ -241,8 +303,7 @@
NM_COMPILER_WARNINGS
-
-dnl
+dnl-----------------------------------------------------------------------------
dnl Distribution version string
dnl
AC_ARG_WITH(dist-version, AS_HELP_STRING([--with-dist-version=<mm-dist-version>], [Define the custom version (like distribution package name and revision)]), ac_distver=$withval, ac_distver="")
@@ -250,10 +311,17 @@
AC_DEFINE_UNQUOTED(MM_DIST_VERSION, "$ac_distver", [Define the distribution version string])
fi
-# Not building protocol libs standalone
+dnl-----------------------------------------------------------------------------
+dnl Protocol libs
+dnl
+
AM_CONDITIONAL(QCDM_STANDALONE, test "yes" = "no")
AM_CONDITIONAL(WMC_STANDALONE, test "yes" = "no")
+dnl-----------------------------------------------------------------------------
+dnl Protocol libs
+dnl
+
AC_CONFIG_FILES([
Makefile
data/Makefile
@@ -302,24 +370,31 @@
ModemManager $VERSION
==============================================
- compiler: ${CC}
- cflags: ${CFLAGS}
- Maintainer mode: ${USE_MAINTAINER_MODE}
- Code coverage: ${CODE_COVERAGE_ENABLED}"
+ Build:
+ compiler: ${CC}
+ cflags: ${CFLAGS}
+ ldflags: ${LDFLAGS}
+ maintainer mode: ${USE_MAINTAINER_MODE}
+
+ System paths:
+ prefix: ${prefix}
+ D-Bus system directory: ${DBUS_SYS_DIR}
+ udev base directory: ${UDEV_BASE_DIR}
+ systemd unit directory: ${with_systemdsystemunitdir}
+
+ Features:
+ policykit support: ${with_polkit}
+ mbim support: ${with_mbim}
+ qmi support: ${with_qmi}
+ suspend/resume support: ${with_suspend_resume}
+
+ Miscellaneous:
+ gobject introspection: ${found_introspection}
+ vala bindings: ${enable_vala}
+ documentation: ${enable_gtk_doc}
+ code coverage: ${CODE_COVERAGE_ENABLED}"
if test "x${CODE_COVERAGE_ENABLED}" = "xyes"; then
- echo " Code coverage cflags: ${CODE_COVERAGE_CFLAGS}"
- echo " Code coverage ldflags: ${CODE_COVERAGE_LDFLAGS}"
+ echo " code coverage cflags: ${CODE_COVERAGE_CFLAGS}"
+ echo " code coverage ldflags: ${CODE_COVERAGE_LDFLAGS}"
fi
-
-echo "
- D-Bus system directory: ${DBUS_SYS_DIR}
- udev base directory: ${UDEV_BASE_DIR}
- systemd unit directory: ${with_systemdsystemunitdir}
-
- PolicyKit support: ${with_polkit}
- GObject Introspection: ${found_introspection}
- Vala Bindings: ${enable_vala}
- Documentation: ${enable_gtk_doc}
- MBIM support: ${with_mbim}
- QMI support: ${with_qmi}
-"
+echo ""
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index a3733ee..1e30b1a 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -126,10 +126,10 @@
libmm-plugin-nokia-icera.la \
libmm-plugin-cinterion.la \
libmm-plugin-iridium.la \
- libmm-plugin-gobi.la \
libmm-plugin-motorola.la \
libmm-plugin-novatel.la \
libmm-plugin-novatel-lte.la \
+ libmm-plugin-dell.la \
libmm-plugin-altair-lte.la \
libmm-plugin-samsung.la \
libmm-plugin-option.la \
@@ -144,6 +144,7 @@
libmm-plugin-pantech.la \
libmm-plugin-zte.la \
libmm-plugin-sierra.la \
+ libmm-plugin-sierra-legacy.la \
libmm-plugin-mbm.la \
libmm-plugin-via.la \
libmm-plugin-telit.la \
@@ -172,15 +173,6 @@
libmm_plugin_motorola_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
libmm_plugin_motorola_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Gobi
-libmm_plugin_gobi_la_SOURCES = \
- gobi/mm-plugin-gobi.c \
- gobi/mm-plugin-gobi.h \
- gobi/mm-broadband-modem-gobi.c \
- gobi/mm-broadband-modem-gobi.h
-libmm_plugin_gobi_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_gobi_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-
# Huawei
libmm_plugin_huawei_la_SOURCES = \
huawei/mm-plugin-huawei.c \
@@ -211,10 +203,9 @@
$(top_builddir)/src/libmodem-helpers.la
test_modem_helpers_huawei_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# MBM
-libmm_plugin_mbm_la_SOURCES = \
- mbm/mm-plugin-mbm.c \
- mbm/mm-plugin-mbm.h \
+# Common Mbm modem support library
+noinst_LTLIBRARIES += libmm-utils-mbm.la
+libmm_utils_mbm_la_SOURCES = \
mbm/mm-broadband-modem-mbm.c \
mbm/mm-broadband-modem-mbm.h \
mbm/mm-broadband-bearer-mbm.c \
@@ -223,8 +214,20 @@
mbm/mm-modem-helpers-mbm.h \
mbm/mm-sim-mbm.c \
mbm/mm-sim-mbm.h
-libmm_plugin_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_utils_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_utils_mbm_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS)
+
+MBM_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/mbm
+MBM_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-mbm.la
+
+# MBM
+libmm_plugin_mbm_la_SOURCES = \
+ mbm/mm-plugin-mbm.c \
+ mbm/mm-plugin-mbm.h
+libmm_plugin_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(MBM_COMMON_COMPILER_FLAGS)
libmm_plugin_mbm_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_mbm_la_LIBADD = $(MBM_COMMON_LIBADD_FLAGS)
+
udevrules_DATA += mbm/77-mm-ericsson-mbm.rules
noinst_PROGRAMS += test-modem-helpers-mbm
@@ -262,10 +265,9 @@
libmm_plugin_hso_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
libmm_plugin_hso_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Sierra
-libmm_plugin_sierra_la_SOURCES = \
- sierra/mm-plugin-sierra.c \
- sierra/mm-plugin-sierra.h \
+# Common Sierra modem support library
+noinst_LTLIBRARIES += libmm-utils-sierra.la
+libmm_utils_sierra_la_SOURCES = \
sierra/mm-common-sierra.c \
sierra/mm-common-sierra.h \
sierra/mm-sim-sierra.c \
@@ -273,12 +275,29 @@
sierra/mm-broadband-bearer-sierra.c \
sierra/mm-broadband-bearer-sierra.h \
sierra/mm-broadband-modem-sierra.c \
- sierra/mm-broadband-modem-sierra.h \
+ sierra/mm-broadband-modem-sierra.h
+libmm_utils_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_utils_sierra_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS)
+
+SIERRA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/sierra
+SIERRA_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-sierra.la
+
+# Sierra (new QMI or MBIM modems)
+libmm_plugin_sierra_la_SOURCES = \
+ sierra/mm-plugin-sierra.c \
+ sierra/mm-plugin-sierra.h
+libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+# Sierra (legacy)
+libmm_plugin_sierra_legacy_la_SOURCES = \
+ sierra/mm-plugin-sierra-legacy.c \
+ sierra/mm-plugin-sierra-legacy.h \
sierra/mm-broadband-modem-sierra-icera.c \
sierra/mm-broadband-modem-sierra-icera.h
-libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS)
-libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-libmm_plugin_sierra_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
+libmm_plugin_sierra_legacy_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS) $(SIERRA_COMMON_COMPILER_FLAGS)
+libmm_plugin_sierra_legacy_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_sierra_legacy_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_LIBADD_FLAGS)
# Wavecom (Sierra Airlink)
libmm_plugin_wavecom_la_SOURCES = \
@@ -441,6 +460,19 @@
libmm_plugin_iridium_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
libmm_plugin_iridium_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+# Common Novatel modem support library
+noinst_LTLIBRARIES += libmm-utils-novatel.la
+libmm_utils_novatel_la_SOURCES = \
+ novatel/mm-common-novatel.c \
+ novatel/mm-common-novatel.h \
+ novatel/mm-broadband-modem-novatel.c \
+ novatel/mm-broadband-modem-novatel.h
+libmm_utils_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+libmm_utils_novatel_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS)
+
+NOVATEL_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/novatel
+NOVATEL_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-novatel.la
+
# Novatel LTE modem
libmm_plugin_novatel_lte_la_SOURCES = \
novatel/mm-plugin-novatel-lte.c \
@@ -457,11 +489,18 @@
# Novatel non-LTE modem
libmm_plugin_novatel_la_SOURCES = \
novatel/mm-plugin-novatel.c \
- novatel/mm-plugin-novatel.h \
- novatel/mm-broadband-modem-novatel.c \
- novatel/mm-broadband-modem-novatel.h
-libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+ novatel/mm-plugin-novatel.h
+libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(NOVATEL_COMMON_COMPILER_FLAGS)
libmm_plugin_novatel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_novatel_la_LIBADD = $(NOVATEL_COMMON_LIBADD_FLAGS)
+
+# Dell (e.g. Novatel or Sierra) modem
+libmm_plugin_dell_la_SOURCES = \
+ dell/mm-plugin-dell.c \
+ dell/mm-plugin-dell.h
+libmm_plugin_dell_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(NOVATEL_COMMON_COMPILER_FLAGS) $(SIERRA_COMMON_COMPILER_FLAGS) $(MBM_COMMON_COMPILER_FLAGS)
+libmm_plugin_dell_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_dell_la_LIBADD = $(NOVATEL_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_LIBADD_FLAGS) $(MBM_COMMON_LIBADD_FLAGS)
# Altair LTE modem
libmm_plugin_altair_lte_la_SOURCES = \
diff --git a/plugins/dell/mm-plugin-dell.c b/plugins/dell/mm-plugin-dell.c
new file mode 100644
index 0000000..0dff19b
--- /dev/null
+++ b/plugins/dell/mm-plugin-dell.c
@@ -0,0 +1,448 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-dell.h"
+#include "mm-common-novatel.h"
+#include "mm-private-boxed-types.h"
+#include "mm-broadband-modem.h"
+#include "mm-broadband-modem-novatel.h"
+#include "mm-common-novatel.h"
+#include "mm-broadband-modem-sierra.h"
+#include "mm-common-sierra.h"
+#include "mm-log.h"
+
+#if defined WITH_QMI
+#include "mm-broadband-modem-qmi.h"
+#endif
+
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginDell, mm_plugin_dell, MM_TYPE_PLUGIN)
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+#define TAG_DELL_MANUFACTURER "dell-manufacturer"
+typedef enum {
+ DELL_MANUFACTURER_UNKNOWN = 0,
+ DELL_MANUFACTURER_NOVATEL = 1,
+ DELL_MANUFACTURER_SIERRA = 2,
+ DELL_MANUFACTURER_ERICSSON = 3
+} DellManufacturer;
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortProbe *probe;
+ MMPortSerialAt *port;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ guint gmi_retries;
+ guint cgmi_retries;
+ guint ati_retries;
+} CustomInitContext;
+
+static void
+custom_init_context_complete_and_free (CustomInitContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->port);
+ g_object_unref (ctx->probe);
+ g_object_unref (ctx->result);
+ g_slice_free (CustomInitContext, ctx);
+}
+
+static gboolean
+dell_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+novatel_custom_init_ready (MMPortProbe *probe,
+ GAsyncResult *res,
+ CustomInitContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_common_novatel_custom_init_finish (probe, res, &error))
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+}
+
+static void
+sierra_custom_init_ready (MMPortProbe *probe,
+ GAsyncResult *res,
+ CustomInitContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_common_sierra_custom_init_finish (probe, res, &error))
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+}
+
+static void custom_init_step (CustomInitContext *ctx);
+
+static void
+custom_init_step_next_command (CustomInitContext *ctx)
+{
+ if (ctx->gmi_retries > 0)
+ ctx->gmi_retries = 0;
+ else if (ctx->cgmi_retries > 0)
+ ctx->cgmi_retries = 0;
+ else if (ctx->ati_retries > 0)
+ ctx->ati_retries = 0;
+ custom_init_step (ctx);
+}
+
+static void
+response_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ CustomInitContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+ gchar *lower;
+ DellManufacturer manufacturer;
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* Non-timeout error, jump to next command */
+ if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ mm_dbg ("(Dell) Error probing AT port: %s", error->message);
+ g_error_free (error);
+ custom_init_step_next_command (ctx);
+ return;
+ }
+ /* Directly retry same command on timeout */
+ custom_init_step (ctx);
+ g_error_free (error);
+ return;
+ }
+
+ /* Guess manufacturer from response */
+ lower = g_ascii_strdown (response, -1);
+ if (strstr (lower, "novatel"))
+ manufacturer = DELL_MANUFACTURER_NOVATEL;
+ else if (strstr (lower, "sierra"))
+ manufacturer = DELL_MANUFACTURER_SIERRA;
+ else if (strstr (lower, "ericsson"))
+ manufacturer = DELL_MANUFACTURER_ERICSSON;
+ else
+ manufacturer = DELL_MANUFACTURER_UNKNOWN;
+ g_free (lower);
+
+ /* Tag based on manufacturer */
+ if (manufacturer != DELL_MANUFACTURER_UNKNOWN) {
+ g_object_set_data (G_OBJECT (ctx->probe), TAG_DELL_MANUFACTURER, GUINT_TO_POINTER (manufacturer));
+
+ /* Run additional custom init, if needed */
+
+ if (manufacturer == DELL_MANUFACTURER_NOVATEL) {
+ mm_common_novatel_custom_init (ctx->probe,
+ ctx->port,
+ ctx->cancellable,
+ (GAsyncReadyCallback) novatel_custom_init_ready,
+ ctx);
+ return;
+ }
+
+ if (manufacturer == DELL_MANUFACTURER_SIERRA) {
+ mm_common_sierra_custom_init (ctx->probe,
+ ctx->port,
+ ctx->cancellable,
+ (GAsyncReadyCallback) sierra_custom_init_ready,
+ ctx);
+ return;
+ }
+
+ /* Finish custom_init */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* If we got a response, but we didn't get an expected string, try with next command */
+ custom_init_step_next_command (ctx);
+}
+
+static void
+custom_init_step (CustomInitContext *ctx)
+{
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ mm_dbg ("(Dell) no need to keep on running custom init in (%s)",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+ return;
+ }
+
+#if defined WITH_QMI
+ /* If device has a QMI port, don't run anything else, as we don't care */
+ if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) {
+ mm_dbg ("(Dell) no need to run custom init in (%s): device has QMI port",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ mm_port_probe_set_result_at (ctx->probe, FALSE);
+ mm_port_probe_set_result_qcdm (ctx->probe, FALSE);
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+ return;
+ }
+#endif
+
+#if defined WITH_MBIM
+ /* If device has a MBIM port, don't run anything else, as we don't care */
+ if (mm_port_probe_list_has_mbim_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) {
+ mm_dbg ("(Dell) no need to run custom init in (%s): device has MBIM port",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ mm_port_probe_set_result_at (ctx->probe, FALSE);
+ mm_port_probe_set_result_qcdm (ctx->probe, FALSE);
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+ return;
+ }
+#endif
+
+ if (ctx->gmi_retries > 0) {
+ ctx->gmi_retries--;
+ mm_port_serial_at_command (ctx->port,
+ "AT+GMI",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ ctx->cancellable,
+ (GAsyncReadyCallback)response_ready,
+ ctx);
+ return;
+ }
+
+ if (ctx->cgmi_retries > 0) {
+ ctx->cgmi_retries--;
+ mm_port_serial_at_command (ctx->port,
+ "AT+CGMI",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ ctx->cancellable,
+ (GAsyncReadyCallback)response_ready,
+ ctx);
+ return;
+ }
+
+ if (ctx->ati_retries > 0) {
+ ctx->ati_retries--;
+ /* Note: in Ericsson devices, ATI3 seems to reply the vendor string */
+ mm_port_serial_at_command (ctx->port,
+ "ATI1I2I3",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ ctx->cancellable,
+ (GAsyncReadyCallback)response_ready,
+ ctx);
+ return;
+ }
+
+ /* Finish custom_init */
+ mm_dbg ("(Dell) couldn't flip secondary port to AT in (%s): all retries consumed",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+}
+
+static void
+dell_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CustomInitContext *ctx;
+
+ ctx = g_slice_new (CustomInitContext);
+ ctx->result = g_simple_async_result_new (G_OBJECT (probe),
+ callback,
+ user_data,
+ dell_custom_init);
+ ctx->probe = g_object_ref (probe);
+ ctx->port = g_object_ref (port);
+ ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ ctx->gmi_retries = 3;
+ ctx->ati_retries = 3;
+
+ custom_init_step (ctx);
+}
+
+/*****************************************************************************/
+
+static gboolean
+port_probe_list_has_manufacturer_port (GList *probes,
+ DellManufacturer manufacturer)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), TAG_DELL_MANUFACTURER)) == manufacturer)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *sysfs_path,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ /* Note: at this point we don't make any difference between different
+ * Dell-branded QMI or MBIM modems; they may come from Novatel, Ericsson or
+ * Sierra. */
+
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_dbg ("QMI-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ mm_dbg ("MBIM-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_NOVATEL)) {
+ mm_dbg ("Novatel-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_novatel_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_SIERRA)) {
+ mm_dbg ("Sierra-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ mm_dbg ("Dell-branded generic modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+static gboolean
+grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error)
+{
+ /* Only Sierra needs custom grab port, due to the port type hints */
+ if (MM_IS_BROADBAND_MODEM_SIERRA (modem))
+ return mm_common_sierra_grab_port (self, modem, probe, error);
+
+ return mm_base_modem_grab_port (modem,
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe),
+ mm_port_probe_get_parent_path (probe),
+ mm_port_probe_get_port_type (probe),
+ MM_PORT_SERIAL_AT_FLAG_NONE,
+ error);
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const guint16 vendors[] = { 0x413c, 0 };
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (dell_custom_init),
+ .finish = G_CALLBACK (dell_custom_init_finish),
+ };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_DELL,
+ MM_PLUGIN_NAME, "Dell",
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_dell_init (MMPluginDell *self)
+{
+}
+
+static void
+mm_plugin_dell_class_init (MMPluginDellClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+ plugin_class->grab_port = grab_port;
+}
diff --git a/plugins/dell/mm-plugin-dell.h b/plugins/dell/mm-plugin-dell.h
new file mode 100644
index 0000000..cc1a539
--- /dev/null
+++ b/plugins/dell/mm-plugin-dell.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_DELL_H
+#define MM_PLUGIN_DELL_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_DELL (mm_plugin_dell_get_type ())
+#define MM_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDell))
+#define MM_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_DELL, MMPluginDellClass))
+#define MM_IS_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_DELL))
+#define MM_IS_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_DELL))
+#define MM_PLUGIN_DELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDellClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginDell;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginDellClass;
+
+GType mm_plugin_dell_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_DELL_H */
diff --git a/plugins/gobi/mm-broadband-modem-gobi.c b/plugins/gobi/mm-broadband-modem-gobi.c
deleted file mode 100644
index 27aded2..0000000
--- a/plugins/gobi/mm-broadband-modem-gobi.c
+++ /dev/null
@@ -1,124 +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 - 2009 Novell, Inc.
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Google Inc.
- */
-
-#include <config.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#include "ModemManager.h"
-#include "mm-modem-helpers.h"
-#include "mm-serial-parsers.h"
-#include "mm-log.h"
-#include "mm-errors-types.h"
-#include "mm-iface-modem.h"
-#include "mm-iface-modem-3gpp.h"
-#include "mm-base-modem-at.h"
-#include "mm-broadband-modem-gobi.h"
-
-static void iface_modem_init (MMIfaceModem *iface);
-
-G_DEFINE_TYPE_EXTENDED (MMBroadbandModemGobi, mm_broadband_modem_gobi, MM_TYPE_BROADBAND_MODEM, 0,
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init));
-
-/*****************************************************************************/
-/* Load access technologies (Modem interface) */
-
-static gboolean
-load_access_technologies_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemAccessTechnology *access_technologies,
- guint *mask,
- GError **error)
-{
- const gchar *p;
- const gchar *response;
-
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
- if (!response)
- return FALSE;
-
- p = mm_strip_tag (response, "*CNTI:");
- p = strchr (p, ',');
- if (p) {
- /* We are reporting ALL 3GPP access technologies here */
- *access_technologies = mm_string_to_access_tech (p + 1);
- *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
- return TRUE;
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse access technologies result: '%s'",
- response);
- return FALSE;
-}
-
-static void
-load_access_technologies (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "*CNTI=0",
- 3,
- FALSE,
- callback,
- user_data);
-}
-
-/*****************************************************************************/
-
-MMBroadbandModemGobi *
-mm_broadband_modem_gobi_new (const gchar *device,
- const gchar **drivers,
- const gchar *plugin,
- guint16 vendor_id,
- guint16 product_id)
-{
- return g_object_new (MM_TYPE_BROADBAND_MODEM_GOBI,
- MM_BASE_MODEM_DEVICE, device,
- MM_BASE_MODEM_DRIVERS, drivers,
- MM_BASE_MODEM_PLUGIN, plugin,
- MM_BASE_MODEM_VENDOR_ID, vendor_id,
- MM_BASE_MODEM_PRODUCT_ID, product_id,
- NULL);
-}
-
-static void
-mm_broadband_modem_gobi_init (MMBroadbandModemGobi *self)
-{
-}
-
-static void
-iface_modem_init (MMIfaceModem *iface)
-{
- iface->load_access_technologies = load_access_technologies;
- iface->load_access_technologies_finish = load_access_technologies_finish;
-
- iface->modem_power_down = NULL;
- iface->modem_power_down_finish = NULL;
-}
-
-static void
-mm_broadband_modem_gobi_class_init (MMBroadbandModemGobiClass *klass)
-{
-}
diff --git a/plugins/gobi/mm-broadband-modem-gobi.h b/plugins/gobi/mm-broadband-modem-gobi.h
deleted file mode 100644
index 4164cfe..0000000
--- a/plugins/gobi/mm-broadband-modem-gobi.h
+++ /dev/null
@@ -1,49 +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 - 2009 Novell, Inc.
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2011 Google Inc.
- */
-
-#ifndef MM_BROADBAND_MODEM_GOBI_H
-#define MM_BROADBAND_MODEM_GOBI_H
-
-#include "mm-broadband-modem.h"
-
-#define MM_TYPE_BROADBAND_MODEM_GOBI (mm_broadband_modem_gobi_get_type ())
-#define MM_BROADBAND_MODEM_GOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_GOBI, MMBroadbandModemGobi))
-#define MM_BROADBAND_MODEM_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_GOBI, MMBroadbandModemGobiClass))
-#define MM_IS_BROADBAND_MODEM_GOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_GOBI))
-#define MM_IS_BROADBAND_MODEM_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_GOBI))
-#define MM_BROADBAND_MODEM_GOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_GOBI, MMBroadbandModemGobiClass))
-
-typedef struct _MMBroadbandModemGobi MMBroadbandModemGobi;
-typedef struct _MMBroadbandModemGobiClass MMBroadbandModemGobiClass;
-
-struct _MMBroadbandModemGobi {
- MMBroadbandModem parent;
-};
-
-struct _MMBroadbandModemGobiClass{
- MMBroadbandModemClass parent;
-};
-
-GType mm_broadband_modem_gobi_get_type (void);
-
-MMBroadbandModemGobi *mm_broadband_modem_gobi_new (const gchar *device,
- const gchar **drivers,
- const gchar *plugin,
- guint16 vendor_id,
- guint16 product_id);
-
-#endif /* MM_BROADBAND_MODEM_GOBI_H */
diff --git a/plugins/gobi/mm-plugin-gobi.c b/plugins/gobi/mm-plugin-gobi.c
deleted file mode 100644
index 823b95d..0000000
--- a/plugins/gobi/mm-plugin-gobi.c
+++ /dev/null
@@ -1,95 +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 - 2009 Novell, Inc.
- * Copyright (C) 2011 Red Hat, Inc.
- */
-
-#include <string.h>
-#include <gmodule.h>
-
-#define _LIBMM_INSIDE_MM
-#include <libmm-glib.h>
-
-#include "mm-plugin-gobi.h"
-#include "mm-broadband-modem-gobi.h"
-#include "mm-log.h"
-
-#if defined WITH_QMI
-#include "mm-broadband-modem-qmi.h"
-#endif
-
-G_DEFINE_TYPE (MMPluginGobi, mm_plugin_gobi, MM_TYPE_PLUGIN)
-
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
-
-/*****************************************************************************/
-
-static MMBaseModem *
-create_modem (MMPlugin *self,
- const gchar *sysfs_path,
- const gchar **drivers,
- guint16 vendor,
- guint16 product,
- GList *probes,
- GError **error)
-{
-#if defined WITH_QMI
- if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Gobi modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
- }
-#endif
-
- return MM_BASE_MODEM (mm_broadband_modem_gobi_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
-}
-
-/*****************************************************************************/
-
-G_MODULE_EXPORT MMPlugin *
-mm_plugin_create (void)
-{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
- static const gchar *drivers[] = { "qcserial", NULL };
-
- return MM_PLUGIN (
- g_object_new (MM_TYPE_PLUGIN_GOBI,
- MM_PLUGIN_NAME, "Gobi",
- MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
- MM_PLUGIN_ALLOWED_DRIVERS, drivers,
- MM_PLUGIN_ALLOWED_AT, TRUE,
- MM_PLUGIN_ALLOWED_QCDM, TRUE,
- MM_PLUGIN_ALLOWED_QMI, TRUE,
- NULL));
-}
-
-static void
-mm_plugin_gobi_init (MMPluginGobi *self)
-{
-}
-
-static void
-mm_plugin_gobi_class_init (MMPluginGobiClass *klass)
-{
- MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
-
- plugin_class->create_modem = create_modem;
-}
diff --git a/plugins/gobi/mm-plugin-gobi.h b/plugins/gobi/mm-plugin-gobi.h
deleted file mode 100644
index c47b172..0000000
--- a/plugins/gobi/mm-plugin-gobi.h
+++ /dev/null
@@ -1,41 +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 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
- */
-
-#ifndef MM_PLUGIN_GOBI_H
-#define MM_PLUGIN_GOBI_H
-
-#include "mm-plugin.h"
-
-#define MM_TYPE_PLUGIN_GOBI (mm_plugin_gobi_get_type ())
-#define MM_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobi))
-#define MM_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass))
-#define MM_IS_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GOBI))
-#define MM_IS_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GOBI))
-#define MM_PLUGIN_GOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass))
-
-typedef struct {
- MMPlugin parent;
-} MMPluginGobi;
-
-typedef struct {
- MMPluginClass parent;
-} MMPluginGobiClass;
-
-GType mm_plugin_gobi_get_type (void);
-
-G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
-
-#endif /* MM_PLUGIN_GOBI_H */
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c
index 7612e64..5362c55 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.c
+++ b/plugins/huawei/mm-modem-helpers-huawei.c
@@ -42,8 +42,8 @@
GError *inner_error = NULL;
if (!response ||
- !(g_str_has_prefix (response, "^NDISSTAT:") ||
- g_str_has_prefix (response, "^NDISSTATQRY:"))) {
+ !(g_ascii_strncasecmp (response, "^NDISSTAT:", strlen ("^NDISSTAT:")) == 0 ||
+ g_ascii_strncasecmp (response, "^NDISSTATQRY:", strlen ("^NDISSTATQRY:")) == 0)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTAT / ^NDISSTATQRY prefix");
return FALSE;
}
@@ -61,47 +61,81 @@
* Or, in newer firmwares:
* ^NDISSTATQRY:0,,,"IPV4",0,,,"IPV6"
* OK
+ *
+ * Or, even (handled separately):
+ * ^NDISSTATQry:1
+ * OK
*/
- r = g_regex_new ("\\^NDISSTAT(?:QRY)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?"
- "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?",
- G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
- 0, NULL);
- g_assert (r != NULL);
- g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
- if (!inner_error && g_match_info_matches (match_info)) {
- guint ip_type_field = 4;
+ /* If multiple fields available, try first parsing method */
+ if (strchr (response, ',')) {
+ r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?"
+ "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (r != NULL);
- /* IPv4 and IPv6 are fields 4 and (if available) 8 */
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ guint ip_type_field = 4;
- while (!inner_error && ip_type_field <= 8) {
- gchar *ip_type_str;
+ /* IPv4 and IPv6 are fields 4 and (if available) 8 */
+
+ while (!inner_error && ip_type_field <= 8) {
+ gchar *ip_type_str;
+ guint connected;
+
+ ip_type_str = mm_get_string_unquoted_from_match_info (match_info, ip_type_field);
+ if (!ip_type_str)
+ break;
+
+ if (!mm_get_uint_from_match_info (match_info, (ip_type_field - 3), &connected) ||
+ (connected != 0 && connected != 1)) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields");
+ } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) {
+ *ipv4_available = TRUE;
+ *ipv4_connected = (gboolean)connected;
+ } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) {
+ *ipv6_available = TRUE;
+ *ipv6_connected = (gboolean)connected;
+ }
+
+ g_free (ip_type_str);
+ ip_type_field += 4;
+ }
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
+ /* No separate IPv4/IPv6 info given just connected/not connected */
+ else {
+ r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d)(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
guint connected;
- ip_type_str = mm_get_string_unquoted_from_match_info (match_info, ip_type_field);
- if (!ip_type_str)
- break;
-
- if (!mm_get_uint_from_match_info (match_info, (ip_type_field - 3), &connected) ||
+ if (!mm_get_uint_from_match_info (match_info, 1, &connected) ||
(connected != 0 && connected != 1)) {
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields");
- } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) {
+ } else {
+ /* We'll assume IPv4 */
*ipv4_available = TRUE;
*ipv4_connected = (gboolean)connected;
- } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) {
- *ipv6_available = TRUE;
- *ipv6_connected = (gboolean)connected;
}
-
- g_free (ip_type_str);
- ip_type_field += 4;
}
- }
- g_match_info_free (match_info);
- g_regex_unref (r);
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
if (!ipv4_available && !ipv6_available) {
inner_error = g_error_new (MM_CORE_ERROR,
@@ -1183,4 +1217,3 @@
return ret;
}
-
diff --git a/plugins/huawei/tests/test-modem-helpers-huawei.c b/plugins/huawei/tests/test-modem-helpers-huawei.c
index 9e92eee..19e6dec 100644
--- a/plugins/huawei/tests/test-modem-helpers-huawei.c
+++ b/plugins/huawei/tests/test-modem-helpers-huawei.c
@@ -101,6 +101,10 @@
{ "^NDISSTATQRY: 1,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, TRUE, TRUE, FALSE },
{ "^NDISSTATQRY: 0,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, TRUE },
{ "^NDISSTATQRY: 0,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, FALSE },
+ { "^NDISSTATQry:1", TRUE, TRUE, FALSE, FALSE },
+ { "^NDISSTATQry:1\r\n", TRUE, TRUE, FALSE, FALSE },
+ { "^NDISSTATQry:0", TRUE, FALSE, FALSE, FALSE },
+ { "^NDISSTATQry:0\r\n", TRUE, FALSE, FALSE, FALSE },
{ NULL, FALSE, FALSE, FALSE, FALSE }
};
@@ -420,6 +424,7 @@
found = mm_huawei_parse_prefmode_response (prefmode_response_tests[i].str,
combinations,
&error);
+ g_assert_no_error (error);
g_assert (found != NULL);
g_assert_cmpuint (found->allowed, ==, prefmode_response_tests[i].allowed);
g_assert_cmpuint (found->preferred, ==, prefmode_response_tests[i].preferred);
@@ -672,6 +677,7 @@
combinations,
&error);
+ g_assert_no_error (error);
g_assert (found != NULL);
g_assert_cmpuint (found->allowed, ==, syscfg_response_tests[i].allowed);
g_assert_cmpuint (found->preferred, ==, syscfg_response_tests[i].preferred);
@@ -993,6 +999,7 @@
combinations,
&error);
+ g_assert_no_error (error);
g_assert (found != NULL);
g_assert_cmpuint (found->allowed, ==, syscfgex_response_tests[i].allowed);
g_assert_cmpuint (found->preferred, ==, syscfgex_response_tests[i].preferred);
@@ -1085,8 +1092,10 @@
g_assert (nwtime_tests[i].leap_seconds == mm_network_timezone_get_leap_seconds (tz));
}
- if (iso8601)
- g_free (iso8601);
+ g_free (iso8601);
+
+ if (tz)
+ g_object_unref (tz);
}
}
@@ -1125,11 +1134,10 @@
g_assert (ret == time_tests[i].ret);
g_assert (ret == (error ? FALSE : TRUE));
+ g_clear_error (&error);
g_assert_cmpstr (time_tests[i].iso8601, ==, iso8601);
-
- if (iso8601)
- g_free (iso8601);
+ g_free (iso8601);
}
}
diff --git a/plugins/icera/tests/test-modem-helpers-icera.c b/plugins/icera/tests/test-modem-helpers-icera.c
index e7d1645..2e7b26f 100644
--- a/plugins/icera/tests/test-modem-helpers-icera.c
+++ b/plugins/icera/tests/test-modem-helpers-icera.c
@@ -141,6 +141,7 @@
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1);
g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2);
+ g_object_unref (ipv4);
} else
g_assert (ipv4 == NULL);
@@ -166,6 +167,7 @@
dnslen = g_strv_length ((gchar **) dns);
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv6_dns1);
+ g_object_unref (ipv6);
} else
g_assert (ipv6 == NULL);
}
diff --git a/plugins/mbm/tests/test-modem-helpers-mbm.c b/plugins/mbm/tests/test-modem-helpers-mbm.c
index 2e6dd1a..0c48894 100644
--- a/plugins/mbm/tests/test-modem-helpers-mbm.c
+++ b/plugins/mbm/tests/test-modem-helpers-mbm.c
@@ -96,6 +96,7 @@
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, tests[i].ipv4_dns1);
g_assert_cmpstr (dns[1], ==, tests[i].ipv4_dns2);
+ g_object_unref (ipv4);
} else
g_assert (ipv4 == NULL);
@@ -122,6 +123,7 @@
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, tests[i].ipv6_dns1);
g_assert_cmpstr (dns[1], ==, tests[i].ipv6_dns2);
+ g_object_unref (ipv6);
} else
g_assert (ipv6 == NULL);
}
diff --git a/plugins/novatel/mm-broadband-modem-novatel.c b/plugins/novatel/mm-broadband-modem-novatel.c
index 765595e..9fd14e2 100644
--- a/plugins/novatel/mm-broadband-modem-novatel.c
+++ b/plugins/novatel/mm-broadband-modem-novatel.c
@@ -1231,7 +1231,7 @@
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
}
static void
diff --git a/plugins/novatel/mm-common-novatel.c b/plugins/novatel/mm-common-novatel.c
new file mode 100644
index 0000000..5305d12
--- /dev/null
+++ b/plugins/novatel/mm-common-novatel.c
@@ -0,0 +1,158 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-common-novatel.h"
+#include "mm-log.h"
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortProbe *probe;
+ MMPortSerialAt *port;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ guint nwdmat_retries;
+ guint wait_time;
+} CustomInitContext;
+
+static void
+custom_init_context_complete_and_free (CustomInitContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->port);
+ g_object_unref (ctx->probe);
+ g_object_unref (ctx->result);
+ g_slice_free (CustomInitContext, ctx);
+}
+
+gboolean
+mm_common_novatel_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void custom_init_step (CustomInitContext *ctx);
+
+static void
+nwdmat_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ CustomInitContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ if (g_error_matches (error,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ custom_init_step (ctx);
+ goto out;
+ }
+
+ mm_dbg ("(Novatel) Error flipping secondary ports to AT mode: %s", error->message);
+ }
+
+ /* Finish custom_init */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+
+out:
+ if (error)
+ g_error_free (error);
+}
+
+static gboolean
+custom_init_wait_cb (CustomInitContext *ctx)
+{
+ custom_init_step (ctx);
+ return FALSE;
+}
+
+static void
+custom_init_step (CustomInitContext *ctx)
+{
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ mm_dbg ("(Novatel) no need to keep on running custom init in (%s)",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* If device has a QMI port, don't run $NWDMAT */
+ if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) {
+ mm_dbg ("(Novatel) no need to run custom init in (%s): device has QMI port",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (ctx->wait_time > 0) {
+ ctx->wait_time--;
+ g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, ctx);
+ return;
+ }
+
+ if (ctx->nwdmat_retries > 0) {
+ ctx->nwdmat_retries--;
+ mm_port_serial_at_command (ctx->port,
+ "$NWDMAT=1",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ ctx->cancellable,
+ (GAsyncReadyCallback)nwdmat_ready,
+ ctx);
+ return;
+ }
+
+ /* Finish custom_init */
+ mm_dbg ("(Novatel) couldn't flip secondary port to AT in (%s): all retries consumed",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ custom_init_context_complete_and_free (ctx);
+}
+
+void
+mm_common_novatel_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CustomInitContext *ctx;
+
+ ctx = g_slice_new (CustomInitContext);
+ ctx->result = g_simple_async_result_new (G_OBJECT (probe),
+ callback,
+ user_data,
+ mm_common_novatel_custom_init);
+ ctx->probe = g_object_ref (probe);
+ ctx->port = g_object_ref (port);
+ ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ ctx->nwdmat_retries = 3;
+ ctx->wait_time = 2;
+
+ custom_init_step (ctx);
+}
diff --git a/plugins/novatel/mm-common-novatel.h b/plugins/novatel/mm-common-novatel.h
new file mode 100644
index 0000000..70572fd
--- /dev/null
+++ b/plugins/novatel/mm-common-novatel.h
@@ -0,0 +1,31 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_COMMON_NOVATEL_H
+#define MM_COMMON_NOVATEL_H
+
+#include "glib.h"
+#include "mm-plugin.h"
+
+void mm_common_novatel_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_common_novatel_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error);
+
+#endif /* MM_COMMON_NOVATEL_H */
diff --git a/plugins/novatel/mm-plugin-novatel.c b/plugins/novatel/mm-plugin-novatel.c
index b57c0e8..9476150 100644
--- a/plugins/novatel/mm-plugin-novatel.c
+++ b/plugins/novatel/mm-plugin-novatel.c
@@ -28,6 +28,7 @@
#include <libmm-glib.h>
#include "mm-plugin-novatel.h"
+#include "mm-common-novatel.h"
#include "mm-private-boxed-types.h"
#include "mm-broadband-modem-novatel.h"
#include "mm-log.h"
@@ -42,147 +43,6 @@
int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
/*****************************************************************************/
-/* Custom init */
-
-typedef struct {
- MMPortProbe *probe;
- MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- guint nwdmat_retries;
- guint wait_time;
-} CustomInitContext;
-
-static void
-custom_init_context_complete_and_free (CustomInitContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
- g_slice_free (CustomInitContext, ctx);
-}
-
-static gboolean
-novatel_custom_init_finish (MMPortProbe *probe,
- GAsyncResult *result,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
-}
-
-static void custom_init_step (CustomInitContext *ctx);
-
-static void
-nwdmat_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- CustomInitContext *ctx)
-{
- const gchar *response;
- GError *error = NULL;
-
- response = mm_port_serial_at_command_finish (port, res, &error);
- if (error) {
- if (g_error_matches (error,
- MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
- custom_init_step (ctx);
- goto out;
- }
-
- mm_dbg ("(Novatel) Error flipping secondary ports to AT mode: %s", error->message);
- }
-
- /* Finish custom_init */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
-
-out:
- if (error)
- g_error_free (error);
-}
-
-static gboolean
-custom_init_wait_cb (CustomInitContext *ctx)
-{
- custom_init_step (ctx);
- return FALSE;
-}
-
-static void
-custom_init_step (CustomInitContext *ctx)
-{
- /* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(Novatel) no need to keep on running custom init in (%s)",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
- return;
- }
-
- /* If device has a QMI port, don't run $NWDMAT */
- if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) {
- mm_dbg ("(Novatel) no need to run custom init in (%s): device has QMI port",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
- return;
- }
-
- if (ctx->wait_time > 0) {
- ctx->wait_time--;
- g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, ctx);
- return;
- }
-
- if (ctx->nwdmat_retries > 0) {
- ctx->nwdmat_retries--;
- mm_port_serial_at_command (ctx->port,
- "$NWDMAT=1",
- 3,
- FALSE, /* raw */
- FALSE, /* allow_cached */
- ctx->cancellable,
- (GAsyncReadyCallback)nwdmat_ready,
- ctx);
- return;
- }
-
- /* Finish custom_init */
- mm_dbg ("(Novatel) couldn't flip secondary port to AT in (%s): all retries consumed",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
-}
-
-static void
-novatel_custom_init (MMPortProbe *probe,
- MMPortSerialAt *port,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- CustomInitContext *ctx;
-
- ctx = g_slice_new (CustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- novatel_custom_init);
- ctx->probe = g_object_ref (probe);
- ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- ctx->nwdmat_retries = 3;
- ctx->wait_time = 2;
-
- custom_init_step (ctx);
-}
-
-/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
@@ -217,14 +77,12 @@
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
- static const guint16 vendors[] = { 0x1410, /* Novatel */
- 0x413c, /* Dell */
- 0 };
+ static const guint16 vendors[] = { 0x1410, 0 };
static const mm_uint16_pair forbidden_products[] = { { 0x1410, 0x9010 }, /* Novatel E362 */
{ 0, 0 } };
static const MMAsyncMethod custom_init = {
- .async = G_CALLBACK (novatel_custom_init),
- .finish = G_CALLBACK (novatel_custom_init_finish),
+ .async = G_CALLBACK (mm_common_novatel_custom_init),
+ .finish = G_CALLBACK (mm_common_novatel_custom_init_finish),
};
return MM_PLUGIN (
diff --git a/plugins/sierra/mm-common-sierra.c b/plugins/sierra/mm-common-sierra.c
index 82a5d7b..e712093 100644
--- a/plugins/sierra/mm-common-sierra.c
+++ b/plugins/sierra/mm-common-sierra.c
@@ -15,6 +15,9 @@
* Copyright (C) 2012 Lanedo GmbH
*/
+#include <stdlib.h>
+#include <string.h>
+
#include "mm-common-sierra.h"
#include "mm-base-modem-at.h"
#include "mm-log.h"
@@ -24,6 +27,220 @@
static MMIfaceModem *iface_modem_parent;
/*****************************************************************************/
+/* Custom init and port type hints */
+
+#define TAG_SIERRA_APP_PORT "sierra-app-port"
+#define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok"
+
+gboolean
+mm_common_sierra_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error)
+{
+ MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ MMPortType ptype;
+
+ ptype = mm_port_probe_get_port_type (probe);
+
+ /* Is it a GSM secondary port? */
+ if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) {
+ if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK))
+ pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
+ else
+ pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ } else if (ptype == MM_PORT_TYPE_AT)
+ pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+
+ return mm_base_modem_grab_port (modem,
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe),
+ mm_port_probe_get_parent_path (probe),
+ ptype,
+ pflags,
+ error);
+}
+
+gboolean
+mm_common_sierra_port_probe_list_is_icera (GList *probes)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ /* Only assume the Icera probing check is valid IF the port is not
+ * secondary. This will skip the stupid ports which reply OK to every
+ * AT command, even the one we use to check for Icera support */
+ if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) &&
+ !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+typedef struct {
+ MMPortProbe *probe;
+ MMPortSerialAt *port;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ guint retries;
+} SierraCustomInitContext;
+
+static void
+sierra_custom_init_context_complete_and_free (SierraCustomInitContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->port);
+ g_object_unref (ctx->probe);
+ g_object_unref (ctx->result);
+ g_slice_free (SierraCustomInitContext, ctx);
+}
+
+gboolean
+mm_common_sierra_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void sierra_custom_init_step (SierraCustomInitContext *ctx);
+
+static void
+gcap_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ SierraCustomInitContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* If consumed all tries and the last error was a timeout, assume the
+ * port is not AT */
+ if (ctx->retries == 0 &&
+ g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ mm_port_probe_set_result_at (ctx->probe, FALSE);
+ }
+ /* If reported a hard parse error, this port is definitely not an AT
+ * port, skip trying. */
+ else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) {
+ mm_port_probe_set_result_at (ctx->probe, FALSE);
+ ctx->retries = 0;
+ }
+ /* Some Icera-based devices (eg, USB305) have an AT-style port that
+ * replies to everything with ERROR, so tag as unsupported; sometimes
+ * the real AT ports do this too, so let a retry tag the port as
+ * supported if it responds correctly later. */
+ else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
+ mm_port_probe_set_result_at (ctx->probe, FALSE);
+ }
+
+ /* Just retry... */
+ sierra_custom_init_step (ctx);
+ goto out;
+ }
+
+ /* A valid reply to ATI tells us this is an AT port already */
+ mm_port_probe_set_result_at (ctx->probe, TRUE);
+
+ /* Sierra APPx ports have limited AT command parsers that just reply with
+ * "OK" to most commands. These can sometimes be used for PPP while the
+ * main port is used for status and control, but older modems tend to crash
+ * or fail PPP. So we whitelist modems that are known to allow PPP on the
+ * secondary APP ports.
+ */
+ if (strstr (response, "APP1")) {
+ g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
+
+ /* PPP-on-APP1-port whitelist */
+ if (strstr (response, "C885") ||
+ strstr (response, "USB 306") ||
+ strstr (response, "MC8790"))
+ g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
+
+ /* For debugging: let users figure out if their device supports PPP
+ * on the APP1 port or not.
+ */
+ if (getenv ("MM_SIERRA_APP1_PPP_OK")) {
+ mm_dbg ("Sierra: APP1 PPP OK '%s'", response);
+ g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
+ }
+ } else if (strstr (response, "APP2") ||
+ strstr (response, "APP3") ||
+ strstr (response, "APP4")) {
+ /* Additional APP ports don't support most AT commands, so they cannot
+ * be used as the primary port.
+ */
+ g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
+ }
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ sierra_custom_init_context_complete_and_free (ctx);
+
+out:
+ if (error)
+ g_error_free (error);
+}
+
+static void
+sierra_custom_init_step (SierraCustomInitContext *ctx)
+{
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ mm_dbg ("(Sierra) no need to keep on running custom init in '%s'",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ sierra_custom_init_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (ctx->retries == 0) {
+ mm_dbg ("(Sierra) Couldn't get port type hints from '%s'",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ sierra_custom_init_context_complete_and_free (ctx);
+ return;
+ }
+
+ ctx->retries--;
+ mm_port_serial_at_command (
+ ctx->port,
+ "ATI",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ ctx->cancellable,
+ (GAsyncReadyCallback)gcap_ready,
+ ctx);
+}
+
+void
+mm_common_sierra_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SierraCustomInitContext *ctx;
+
+ ctx = g_slice_new (SierraCustomInitContext);
+ ctx->result = g_simple_async_result_new (G_OBJECT (probe),
+ callback,
+ user_data,
+ mm_common_sierra_custom_init);
+ ctx->probe = g_object_ref (probe);
+ ctx->port = g_object_ref (port);
+ ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ ctx->retries = 3;
+
+ sierra_custom_init_step (ctx);
+}
+
+/*****************************************************************************/
/* Modem power up (Modem interface) */
gboolean
diff --git a/plugins/sierra/mm-common-sierra.h b/plugins/sierra/mm-common-sierra.h
index ec206d2..22471c0 100644
--- a/plugins/sierra/mm-common-sierra.h
+++ b/plugins/sierra/mm-common-sierra.h
@@ -18,10 +18,27 @@
#ifndef MM_COMMON_SIERRA_H
#define MM_COMMON_SIERRA_H
+#include "mm-plugin.h"
#include "mm-broadband-modem.h"
#include "mm-iface-modem.h"
#include "mm-base-sim.h"
+gboolean mm_common_sierra_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error);
+
+gboolean mm_common_sierra_port_probe_list_is_icera (GList *probes);
+
+void mm_common_sierra_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_common_sierra_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error);
+
void mm_common_sierra_load_power_state (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data);
diff --git a/plugins/sierra/mm-plugin-sierra-legacy.c b/plugins/sierra/mm-plugin-sierra-legacy.c
new file mode 100644
index 0000000..9064bcf
--- /dev/null
+++ b/plugins/sierra/mm-plugin-sierra-legacy.c
@@ -0,0 +1,99 @@
+/* -*- 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 - 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-plugin-sierra-legacy.h"
+#include "mm-common-sierra.h"
+#include "mm-broadband-modem-sierra.h"
+#include "mm-broadband-modem-sierra-icera.h"
+
+G_DEFINE_TYPE (MMPluginSierraLegacy, mm_plugin_sierra_legacy, MM_TYPE_PLUGIN)
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *sysfs_path,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ if (mm_common_sierra_port_probe_list_is_icera (probes))
+ return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+
+ return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", NULL };
+ static const gchar *drivers[] = { "sierra", "sierra_net", NULL };
+ static const gchar *forbidden_drivers[] = { "qmi_wwan", "cdc_mbim", NULL };
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (mm_common_sierra_custom_init),
+ .finish = G_CALLBACK (mm_common_sierra_custom_init_finish),
+ };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_SIERRA_LEGACY,
+ MM_PLUGIN_NAME, "Sierra (legacy)",
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_DRIVERS, drivers,
+ MM_PLUGIN_FORBIDDEN_DRIVERS, forbidden_drivers,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ MM_PLUGIN_ICERA_PROBE, TRUE,
+ MM_PLUGIN_REMOVE_ECHO, FALSE,
+ NULL));
+}
+
+static void
+mm_plugin_sierra_legacy_init (MMPluginSierraLegacy *self)
+{
+}
+
+static void
+mm_plugin_sierra_legacy_class_init (MMPluginSierraLegacyClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+ plugin_class->grab_port = mm_common_sierra_grab_port;
+}
diff --git a/plugins/sierra/mm-plugin-sierra-legacy.h b/plugins/sierra/mm-plugin-sierra-legacy.h
new file mode 100644
index 0000000..787118d
--- /dev/null
+++ b/plugins/sierra/mm-plugin-sierra-legacy.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_SIERRA_LEGACY_H
+#define MM_PLUGIN_SIERRA_LEGACY_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_SIERRA_LEGACY (mm_plugin_sierra_legacy_get_type ())
+#define MM_PLUGIN_SIERRA_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacy))
+#define MM_PLUGIN_SIERRA_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacyClass))
+#define MM_IS_PLUGIN_SIERRA_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY))
+#define MM_IS_PLUGIN_SIERRA_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIERRA_LEGACY))
+#define MM_PLUGIN_SIERRA_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacyClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginSierraLegacy;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginSierraLegacyClass;
+
+GType mm_plugin_sierra_legacy_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_SIERRA_LEGACY_H */
diff --git a/plugins/sierra/mm-plugin-sierra.c b/plugins/sierra/mm-plugin-sierra.c
index 96f657a..62262ae 100644
--- a/plugins/sierra/mm-plugin-sierra.c
+++ b/plugins/sierra/mm-plugin-sierra.c
@@ -13,9 +13,9 @@
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
*/
-#include <string.h>
#include <stdlib.h>
#include <gmodule.h>
@@ -24,8 +24,6 @@
#include "mm-log.h"
#include "mm-plugin-sierra.h"
-#include "mm-broadband-modem-sierra.h"
-#include "mm-broadband-modem-sierra-icera.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
@@ -41,191 +39,6 @@
int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
/*****************************************************************************/
-/* Custom init */
-
-#define TAG_SIERRA_APP_PORT "sierra-app-port"
-#define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok"
-
-typedef struct {
- MMPortProbe *probe;
- MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- guint retries;
-} SierraCustomInitContext;
-
-static void
-sierra_custom_init_context_complete_and_free (SierraCustomInitContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
- g_slice_free (SierraCustomInitContext, ctx);
-}
-
-static gboolean
-sierra_custom_init_finish (MMPortProbe *probe,
- GAsyncResult *result,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
-}
-
-static void sierra_custom_init_step (SierraCustomInitContext *ctx);
-
-static void
-gcap_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- SierraCustomInitContext *ctx)
-{
- const gchar *response;
- GError *error = NULL;
-
- response = mm_port_serial_at_command_finish (port, res, &error);
- if (error) {
- /* If consumed all tries and the last error was a timeout, assume the
- * port is not AT */
- if (ctx->retries == 0 &&
- g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- }
- /* If reported a hard parse error, this port is definitely not an AT
- * port, skip trying. */
- else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) {
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- ctx->retries = 0;
- }
- /* Some Icera-based devices (eg, USB305) have an AT-style port that
- * replies to everything with ERROR, so tag as unsupported; sometimes
- * the real AT ports do this too, so let a retry tag the port as
- * supported if it responds correctly later. */
- else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- }
-
- /* Just retry... */
- sierra_custom_init_step (ctx);
- goto out;
- }
-
- /* A valid reply to ATI tells us this is an AT port already */
- mm_port_probe_set_result_at (ctx->probe, TRUE);
-
- /* Sierra APPx ports have limited AT command parsers that just reply with
- * "OK" to most commands. These can sometimes be used for PPP while the
- * main port is used for status and control, but older modems tend to crash
- * or fail PPP. So we whitelist modems that are known to allow PPP on the
- * secondary APP ports.
- */
- if (strstr (response, "APP1")) {
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
-
- /* PPP-on-APP1-port whitelist */
- if (strstr (response, "C885") ||
- strstr (response, "USB 306") ||
- strstr (response, "MC8790"))
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
-
- /* For debugging: let users figure out if their device supports PPP
- * on the APP1 port or not.
- */
- if (getenv ("MM_SIERRA_APP1_PPP_OK")) {
- mm_dbg ("Sierra: APP1 PPP OK '%s'", response);
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
- }
- } else if (strstr (response, "APP2") ||
- strstr (response, "APP3") ||
- strstr (response, "APP4")) {
- /* Additional APP ports don't support most AT commands, so they cannot
- * be used as the primary port.
- */
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
- }
-
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sierra_custom_init_context_complete_and_free (ctx);
-
-out:
- if (error)
- g_error_free (error);
-}
-
-static void
-sierra_custom_init_step (SierraCustomInitContext *ctx)
-{
- /* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(Sierra) no need to keep on running custom init in '%s'",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sierra_custom_init_context_complete_and_free (ctx);
- return;
- }
-
- if (ctx->retries == 0) {
- mm_dbg ("(Sierra) Couldn't get port type hints from '%s'",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sierra_custom_init_context_complete_and_free (ctx);
- return;
- }
-
- ctx->retries--;
- mm_port_serial_at_command (
- ctx->port,
- "ATI",
- 3,
- FALSE, /* raw */
- FALSE, /* allow_cached */
- ctx->cancellable,
- (GAsyncReadyCallback)gcap_ready,
- ctx);
-}
-
-static void
-sierra_custom_init (MMPortProbe *probe,
- MMPortSerialAt *port,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SierraCustomInitContext *ctx;
-
- ctx = g_slice_new (SierraCustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- sierra_custom_init);
- ctx->probe = g_object_ref (probe);
- ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- ctx->retries = 3;
-
- sierra_custom_init_step (ctx);
-}
-
-/*****************************************************************************/
-
-static gboolean
-sierra_port_probe_list_is_icera (GList *probes)
-{
- GList *l;
-
- for (l = probes; l; l = g_list_next (l)) {
- /* Only assume the Icera probing check is valid IF the port is not
- * secondary. This will skip the stupid ports which reply OK to every
- * AT command, even the one we use to check for Icera support */
- if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) &&
- !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT))
- return TRUE;
- }
-
- return FALSE;
-}
static MMBaseModem *
create_modem (MMPlugin *self,
@@ -258,47 +71,12 @@
}
#endif
- if (sierra_port_probe_list_is_icera (probes))
- return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
-
- return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
-}
-
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
- MMPortType ptype;
-
- ptype = mm_port_probe_get_port_type (probe);
-
- /* Is it a GSM secondary port? */
- if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) {
- if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK))
- pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
- else
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else if (ptype == MM_PORT_TYPE_AT)
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- ptype,
- pflags,
- error);
+ /* Fallback to default modem in the worst case */
+ return MM_BASE_MODEM (mm_broadband_modem_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
}
/*****************************************************************************/
@@ -307,24 +85,19 @@
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
- static const gchar *drivers[] = { "sierra", "sierra_net", NULL };
- static const MMAsyncMethod custom_init = {
- .async = G_CALLBACK (sierra_custom_init),
- .finish = G_CALLBACK (sierra_custom_init_finish),
- };
+ static const guint16 vendor_ids[] = { 0x1199, 0 };
+ static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_SIERRA,
MM_PLUGIN_NAME, "Sierra",
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_DRIVERS, drivers,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
- MM_PLUGIN_CUSTOM_INIT, &custom_init,
- MM_PLUGIN_ICERA_PROBE, TRUE,
- MM_PLUGIN_REMOVE_ECHO, FALSE,
NULL));
}
@@ -339,5 +112,4 @@
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/telit/77-mm-telit-port-types.rules b/plugins/telit/77-mm-telit-port-types.rules
index f06822e..1efb0e1 100644
--- a/plugins/telit/77-mm-telit-port-types.rules
+++ b/plugins/telit/77-mm-telit-port-types.rules
@@ -41,6 +41,9 @@
ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{ID_MM_TELIT_TAGGED}="1"
+# HE910, UE910, UL865 (dynamic port identification supported)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="0021", ENV{ID_MM_TELIT_TAGGED}="1", ENV{ID_MM_TELIT_PORTS_TAGGED}="1"
+
# NOTE: Qualcomm Gobi-based devices like the LE920 should not be handled
# by this plugin, but by the Gobi plugin.
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
index 5e1dbff..971e85e 100644
--- a/plugins/telit/mm-broadband-modem-telit.c
+++ b/plugins/telit/mm-broadband-modem-telit.c
@@ -29,12 +29,15 @@
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
#include "mm-broadband-modem-telit.h"
static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemTelit, mm_broadband_modem_telit, MM_TYPE_BROADBAND_MODEM, 0,
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init));
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init));
/*****************************************************************************/
/* Load access technologies (Modem interface) */
@@ -175,6 +178,92 @@
}
/*****************************************************************************/
+/* Flow control (Modem interface) */
+
+static gboolean
+setup_flow_control_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ /* Completely ignore errors */
+ return TRUE;
+}
+
+static void
+setup_flow_control (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gchar *cmd;
+ guint flow_control = 1; /* Default flow control: XON/XOFF */
+
+ switch (mm_base_modem_get_product_id (MM_BASE_MODEM (self)) & 0xFFFF) {
+ case 0x0021:
+ flow_control = 2; /* Telit IMC modems support only RTS/CTS mode */
+ break;
+ default:
+ break;
+ }
+
+ cmd = g_strdup_printf ("+IFC=%u,%u", flow_control, flow_control);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ NULL,
+ NULL);
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ setup_flow_control);
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+/* Enabling unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ /* Ignore errors */
+ mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self),
+ res,
+ NULL,
+ NULL);
+ return TRUE;
+}
+
+static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
+ /* Enable +CIEV only for: signal, service, roam */
+ { "AT+CIND=0,1,1,0,0,0,1,0,0", 5, FALSE, NULL },
+ /* Telit modems +CMER command supports only <ind>=2 */
+ { "+CMER=3,0,0,2", 5, FALSE, NULL },
+ { NULL }
+};
+
+static void
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_sequence_full (
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)),
+ unsolicited_enable_sequence,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ NULL, /* cancellable */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
MMBroadbandModemTelit *
mm_broadband_modem_telit_new (const gchar *device,
@@ -202,6 +291,15 @@
{
iface->load_access_technologies = load_access_technologies;
iface->load_access_technologies_finish = load_access_technologies_finish;
+ iface->setup_flow_control = setup_flow_control;
+ iface->setup_flow_control_finish = setup_flow_control_finish;
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+ iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
}
static void
diff --git a/plugins/telit/mm-plugin-telit.c b/plugins/telit/mm-plugin-telit.c
index 4783095..d9793d1 100644
--- a/plugins/telit/mm-plugin-telit.c
+++ b/plugins/telit/mm-plugin-telit.c
@@ -21,6 +21,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-port-enums-types.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-plugin-telit.h"
@@ -33,6 +34,12 @@
/*****************************************************************************/
+#define TAG_GETPORTCFG_SUPPORTED "getportcfg-supported"
+
+#define TAG_TELIT_MODEM_PORT "ID_MM_TELIT_PORT_TYPE_MODEM"
+#define TAG_TELIT_AUX_PORT "ID_MM_TELIT_PORT_TYPE_AUX"
+#define TAG_TELIT_NMEA_PORT "ID_MM_TELIT_PORT_TYPE_NMEA"
+
static MMBaseModem *
create_modem (MMPlugin *self,
const gchar *sysfs_path,
@@ -56,31 +63,54 @@
GError **error)
{
GUdevDevice *port;
+ MMDevice *device;
MMPortType ptype;
MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
port = mm_port_probe_peek_port (probe);
ptype = mm_port_probe_get_port_type (probe);
+ device = mm_port_probe_peek_device (probe);
/* 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 no udev rules are found, AT#PORTCFG (if supported) can be used for
+ * identifying the port layout
*/
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_TELIT_PORT_TYPE_MODEM")) {
+ if (g_udev_device_get_property_as_boolean (port, TAG_TELIT_MODEM_PORT)) {
mm_dbg ("telit: AT port '%s/%s' flagged as primary",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_TELIT_PORT_TYPE_AUX")) {
+ } else if (g_udev_device_get_property_as_boolean (port, TAG_TELIT_AUX_PORT)) {
mm_dbg ("telit: AT port '%s/%s' flagged as secondary",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_TELIT_PORT_TYPE_NMEA")) {
+ } else if (g_udev_device_get_property_as_boolean (port, TAG_TELIT_NMEA_PORT)) {
mm_dbg ("telit: port '%s/%s' flagged as NMEA",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
ptype = MM_PORT_TYPE_GPS;
+ } else if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) {
+ if (g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT)) == 0) {
+ mm_dbg ("telit: AT port '%s/%s' flagged as primary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ } else if (g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT)) == 0) {
+ mm_dbg ("telit: AT port '%s/%s' flagged as secondary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ } else if (g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT)) == 0) {
+ mm_dbg ("telit: port '%s/%s' flagged as NMEA",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ ptype = MM_PORT_TYPE_GPS;
+ } else
+ ptype = MM_PORT_TYPE_IGNORED;
} else {
/* If the port was tagged by the udev rules but isn't a primary or secondary,
* then ignore it to guard against race conditions if a device just happens
@@ -99,6 +129,225 @@
}
/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortProbe *probe;
+ MMPortSerialAt *port;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ gboolean getportcfg_done;
+ guint getportcfg_retries;
+} TelitCustomInitContext;
+
+static gboolean
+telit_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void telit_custom_init_step (TelitCustomInitContext *ctx);
+
+static gboolean
+cache_port_mode (MMDevice *device,
+ const gchar *reply)
+{
+ GRegex *r = NULL;
+ GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW;
+ GMatchInfo *match_info = NULL;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+ guint portcfg_current;
+
+ /* #PORTCFG: <requested>,<active> */
+ r = g_regex_new ("#PORTCFG:\\s*(\\d+),(\\d+)", flags, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &error))
+ goto out;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &portcfg_current)) {
+ mm_dbg ("telit: unrecognized #PORTCFG <active> value");
+ goto out;
+ }
+
+ /* Reference for port configurations:
+ * HE910/UE910/UL865 Families Ports Arrangements User Guide
+ */
+ switch (portcfg_current) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ case 7:
+ case 9:
+ case 10:
+ case 11:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, "00");
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, "06");
+ break;
+ case 2:
+ case 3:
+ case 6:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, "00");
+ break;
+ case 8:
+ case 12:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, "00");
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, "06");
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, "0a");
+ break;
+ default:
+ /* portcfg value not supported */
+ goto out;
+ }
+ ret = TRUE;
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ if (error != NULL) {
+ mm_dbg ("telit: error while matching: %s", error->message);
+ g_error_free (error);
+ }
+ return ret;
+}
+
+static void
+getportcfg_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ TelitCustomInitContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ mm_dbg ("telit: couldn't get port mode: '%s'",
+ error->message);
+
+ /* If ERROR or COMMAND NOT SUPPORT occur then do not retry the
+ * command.
+ */
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
+ ctx->getportcfg_done = TRUE;
+ } else {
+ MMDevice *device;
+
+ device = mm_port_probe_peek_device (ctx->probe);
+
+ /* Results are cached in the parent device object */
+ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) == NULL) {
+ mm_dbg ("telit: retrieving port mode layout");
+ if (cache_port_mode (device, response)) {
+ g_object_set_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED, GUINT_TO_POINTER (TRUE));
+ ctx->getportcfg_done = TRUE;
+ }
+ }
+
+ /* Port answered to #PORTCFG, so mark it as being AT already */
+ mm_port_probe_set_result_at (ctx->probe, TRUE);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ telit_custom_init_step (ctx);
+}
+
+static void
+telit_custom_init_context_complete_and_free (TelitCustomInitContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->port);
+ g_object_unref (ctx->probe);
+ g_object_unref (ctx->result);
+ g_slice_free (TelitCustomInitContext, ctx);
+}
+
+static void
+telit_custom_init_step (TelitCustomInitContext *ctx)
+{
+ GUdevDevice *port;
+
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ mm_dbg ("telit: no need to keep on running custom init in (%s)",
+ mm_port_get_device (MM_PORT (ctx->port)));
+ goto out;
+ }
+
+ /* Try to get a port configuration from the modem: usb interface 00
+ * is always linked to an AT port
+ */
+ port = mm_port_probe_peek_port (ctx->probe);
+ if (!ctx->getportcfg_done &&
+ g_strcmp0 (g_udev_device_get_property (port, "ID_USB_INTERFACE_NUM"), "00") == 0) {
+
+ if (ctx->getportcfg_retries == 0)
+ goto out;
+ ctx->getportcfg_retries--;
+
+ mm_port_serial_at_command (
+ ctx->port,
+ "AT#PORTCFG?",
+ 2,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ ctx->cancellable,
+ (GAsyncReadyCallback)getportcfg_ready,
+ ctx);
+ return;
+ }
+
+out:
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ telit_custom_init_context_complete_and_free (ctx);
+}
+
+static void
+telit_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMDevice *device;
+ GUdevDevice *udevDevice;
+ TelitCustomInitContext *ctx;
+
+ device = mm_port_probe_peek_device (probe);
+ udevDevice = mm_port_probe_peek_port (probe);
+
+ ctx = g_slice_new (TelitCustomInitContext);
+ ctx->result = g_simple_async_result_new (G_OBJECT (probe),
+ callback,
+ user_data,
+ telit_custom_init);
+ ctx->probe = g_object_ref (probe);
+ ctx->port = g_object_ref (port);
+ ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ ctx->getportcfg_done = FALSE;
+ ctx->getportcfg_retries = 3;
+
+ /* If the device is tagged for supporting #PORTCFG do the custom init */
+ if (g_udev_device_get_property_as_boolean (udevDevice, "ID_MM_TELIT_PORTS_TAGGED")) {
+ telit_custom_init_step (ctx);
+ return;
+ }
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ telit_custom_init_context_complete_and_free (ctx);
+}
+
+/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
@@ -111,6 +360,11 @@
"ID_MM_TELIT_TAGGED",
NULL
};
+ /* Custom init for port identification */
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (telit_custom_init),
+ .finish = G_CALLBACK (telit_custom_init_finish),
+ };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_TELIT,
@@ -119,6 +373,7 @@
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
NULL));
}
diff --git a/plugins/zte/77-mm-zte-port-types.rules b/plugins/zte/77-mm-zte-port-types.rules
index f9d62d8..978a4a4 100644
--- a/plugins/zte/77-mm-zte-port-types.rules
+++ b/plugins/zte/77-mm-zte-port-types.rules
@@ -168,6 +168,9 @@
ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0156", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0156", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+
ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9725a1e..80c4d32 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,3 +2,4 @@
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
data/org.freedesktop.ModemManager1.policy.in.in
+src/mm-sleep-monitor-systemd.c
diff --git a/src/77-mm-usb-device-blacklist.rules b/src/77-mm-usb-device-blacklist.rules
index 4a74714..51db4cd 100644
--- a/src/77-mm-usb-device-blacklist.rules
+++ b/src/77-mm-usb-device-blacklist.rules
@@ -138,4 +138,7 @@
# Palmconnect
ATTRS{idVendor}=="0830", ATTRS{idProduct}=="0080", ENV{ID_MM_DEVICE_IGNORE}="1"
+# IMC flashing device
+ATTRS{idVendor}=="058b", ATTRS{idProduct}=="0041", ENV{ID_MM_DEVICE_IGNORE}="1"
+
LABEL="mm_usb_device_blacklist_end"
diff --git a/src/77-mm-usb-serial-adapters-greylist.rules b/src/77-mm-usb-serial-adapters-greylist.rules
index 3f3e090..d11df52 100644
--- a/src/77-mm-usb-serial-adapters-greylist.rules
+++ b/src/77-mm-usb-serial-adapters-greylist.rules
@@ -35,4 +35,14 @@
# Netchip Technology, Inc. Linux-USB Serial Gadget (CDC ACM mode)
ATTRS{idVendor}=="0525", ATTRS{idProduct}=="a4a7", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+# Cypress Serial-USB devices
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0002", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0004", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0005", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0006", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0007", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="0009", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="04B4", ATTRS{idProduct}=="000A", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+
LABEL="mm_usb_serial_adapters_greylist_end"
diff --git a/src/Makefile.am b/src/Makefile.am
index 3b545ee..ed4e238 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -96,7 +96,9 @@
mm-port-serial-qcdm.c \
mm-port-serial-qcdm.h \
mm-port-serial-gps.c \
- mm-port-serial-gps.h
+ mm-port-serial-gps.h \
+ mm-serial-parsers.c \
+ mm-serial-parsers.h
# Additional QMI support in libserial
if WITH_QMI
@@ -166,6 +168,7 @@
mm-context.c \
mm-log.c \
mm-log.h \
+ mm-utils.h \
mm-private-boxed-types.h \
mm-private-boxed-types.c \
mm-auth.h \
@@ -218,8 +221,6 @@
mm-iface-modem-oma.c \
mm-broadband-modem.h \
mm-broadband-modem.c \
- mm-serial-parsers.c \
- mm-serial-parsers.h \
mm-port-probe.h \
mm-port-probe.c \
mm-port-probe-at.h \
@@ -239,6 +240,16 @@
ModemManager_CPPFLAGS += $(POLKIT_CFLAGS)
endif
+# Additional suspend/resume support via systemd
+if SUSPEND_RESUME_SYSTEMD
+ModemManager_SOURCES += mm-sleep-monitor.h mm-sleep-monitor-systemd.c
+endif
+
+# Additional suspend/resume support via upower
+if SUSPEND_RESUME_UPOWER
+ModemManager_SOURCES += mm-sleep-monitor.h mm-sleep-monitor-upower.c
+endif
+
# Additional QMI support in ModemManager
if WITH_QMI
ModemManager_SOURCES += \
diff --git a/src/main.c b/src/main.c
index 47fa613..8a3ebab 100644
--- a/src/main.c
+++ b/src/main.c
@@ -30,6 +30,10 @@
#include "mm-log.h"
#include "mm-context.h"
+#if WITH_SUSPEND_RESUME
+# include "mm-sleep-monitor.h"
+#endif
+
/* Maximum time to wait for all modems to get disabled and removed */
#define MAX_SHUTDOWN_TIME_SECS 20
@@ -51,6 +55,24 @@
return FALSE;
}
+#if WITH_SUSPEND_RESUME
+
+static void
+sleeping_cb (MMSleepMonitor *sleep_monitor)
+{
+ mm_dbg ("Removing devices... (sleeping)");
+ mm_base_manager_shutdown (manager, FALSE);
+}
+
+static void
+resuming_cb (MMSleepMonitor *sleep_monitor)
+{
+ mm_dbg ("Re-scanning (resuming)");
+ mm_base_manager_start (manager, FALSE);
+}
+
+#endif
+
static void
bus_acquired_cb (GDBusConnection *connection,
const gchar *name,
@@ -144,6 +166,15 @@
name_lost_cb,
NULL,
NULL);
+#if WITH_SUSPEND_RESUME
+ {
+ MMSleepMonitor *sleep_monitor;
+
+ sleep_monitor = mm_sleep_monitor_get ();
+ g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_SLEEPING, G_CALLBACK (sleeping_cb), NULL);
+ g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_cb), NULL);
+ }
+#endif
/* Go into the main loop */
loop = g_main_loop_new (NULL, FALSE);
@@ -157,7 +188,7 @@
if (manager) {
GTimer *timer;
- mm_base_manager_shutdown (manager);
+ mm_base_manager_shutdown (manager, TRUE);
/* Wait for all modems to be disabled and removed, but don't wait
* forever: if disabling the modems takes longer than 20s, just
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index f5d4e28..fdcd037 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -148,7 +148,7 @@
GError *error = NULL;
if (!mm_plugin_manager_find_device_support_finish (plugin_manager, result, &error)) {
- mm_warn ("Couldn't find support for device at '%s': %s",
+ mm_info ("Couldn't find support for device at '%s': %s",
mm_device_get_path (ctx->device),
error->message);
g_error_free (error);
@@ -538,6 +538,7 @@
device = find_device_by_modem (self, modem);
if (device) {
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
mm_device_remove_modem (device);
g_hash_table_remove (self->priv->devices, device);
}
@@ -555,8 +556,23 @@
mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self);
}
+static gboolean
+foreach_remove (gpointer key,
+ MMDevice *device,
+ MMBaseManager *self)
+{
+ MMBaseModem *modem;
+
+ modem = mm_device_peek_modem (device);
+ if (modem)
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
+ mm_device_remove_modem (device);
+ return TRUE;
+}
+
void
-mm_base_manager_shutdown (MMBaseManager *self)
+mm_base_manager_shutdown (MMBaseManager *self,
+ gboolean disable)
{
g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_BASE_MANAGER (self));
@@ -564,12 +580,18 @@
/* Cancel all ongoing auth requests */
g_cancellable_cancel (self->priv->authp_cancellable);
- g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
+ if (disable) {
+ g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
- /* Disabling may take a few iterations of the mainloop, so the caller
- * has to iterate the mainloop until all devices have been disabled and
- * removed.
- */
+ /* Disabling may take a few iterations of the mainloop, so the caller
+ * has to iterate the mainloop until all devices have been disabled and
+ * removed.
+ */
+ return;
+ }
+
+ /* Otherwise, just remove directly */
+ g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self);
}
guint32
diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h
index 0e4e97b..43e7cae 100644
--- a/src/mm-base-manager.h
+++ b/src/mm-base-manager.h
@@ -57,7 +57,8 @@
void mm_base_manager_start (MMBaseManager *manager,
gboolean manual_scan);
-void mm_base_manager_shutdown (MMBaseManager *manager);
+void mm_base_manager_shutdown (MMBaseManager *manager,
+ gboolean disable);
guint32 mm_base_manager_num_modems (MMBaseManager *manager);
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 51abb30..db8e0ac 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -406,7 +406,6 @@
/* IPv6 address */
qmi_inet6_ntop (array, buf, sizeof (buf));
- g_array_unref (array);
mm_bearer_ip_config_set_address (config, buf);
mm_bearer_ip_config_set_prefix (config, prefix);
@@ -416,8 +415,7 @@
if (qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error)) {
qmi_inet6_ntop (array, buf, sizeof (buf));
mm_bearer_ip_config_set_gateway (config, buf);
- mm_dbg (" Gateway: %s", buf);
- g_array_unref (array);
+ mm_dbg (" Gateway: %s/%d", buf, prefix);
} else {
mm_dbg (" Gateway: failed (%s)", error->message);
g_clear_error (&error);
@@ -428,7 +426,6 @@
qmi_inet6_ntop (array, buf, sizeof (buf));
dns[dns_idx++] = buf;
mm_dbg (" DNS #1: %s", buf);
- g_array_unref (array);
} else {
mm_dbg (" DNS #1: failed (%s)", error->message);
g_clear_error (&error);
@@ -439,7 +436,6 @@
qmi_inet6_ntop (array, buf2, sizeof (buf2));
dns[dns_idx++] = buf2;
mm_dbg (" DNS #2: %s", buf2);
- g_array_unref (array);
} else {
mm_dbg (" DNS #2: failed (%s)", error->message);
g_clear_error (&error);
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index d43b71a..4d234c6 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -2481,6 +2481,32 @@
/*****************************************************************************/
/* Powering up the modem (Modem interface) */
+typedef enum {
+ SET_OPERATING_MODE_STEP_FIRST,
+ SET_OPERATING_MODE_STEP_FCC_AUTH,
+ SET_OPERATING_MODE_STEP_RETRY,
+ SET_OPERATING_MODE_STEP_LAST
+} SetOperatingModeStep;
+
+typedef struct {
+ MMBroadbandModemQmi *self;
+ QmiClientDms *client;
+ GSimpleAsyncResult *result;
+ QmiMessageDmsSetOperatingModeInput *input;
+ SetOperatingModeStep step;
+} SetOperatingModeContext;
+
+static void
+set_operating_mode_context_complete_and_free (SetOperatingModeContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->client);
+ g_object_unref (ctx->self);
+ qmi_message_dms_set_operating_mode_input_unref (ctx->input);
+ g_slice_free (SetOperatingModeContext, ctx);
+}
+
static gboolean
modem_power_up_down_off_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -2489,38 +2515,125 @@
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
+static void set_operating_mode_context_step (SetOperatingModeContext *ctx);
+
+static void
+dms_set_fcc_authentication_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ SetOperatingModeContext *ctx)
+{
+ QmiMessageDmsSetFccAuthenticationOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_dms_set_fcc_authentication_finish (client, res, &error);
+ if (!output || !qmi_message_dms_set_fcc_authentication_output_get_result (output, &error)) {
+ /* No hard errors */
+ mm_dbg ("Couldn't set FCC authentication: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (output)
+ qmi_message_dms_set_fcc_authentication_output_unref (output);
+
+ /* Retry Set Operating Mode */
+ ctx->step++;
+ set_operating_mode_context_step (ctx);
+}
+
static void
dms_set_operating_mode_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ SetOperatingModeContext *ctx)
{
QmiMessageDmsSetOperatingModeOutput *output = NULL;
GError *error = NULL;
output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
if (!output) {
- if (g_error_matches (error,
- QMI_CORE_ERROR,
- QMI_CORE_ERROR_UNSUPPORTED)) {
+ /* If unsupported, just go out without errors */
+ if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_UNSUPPORTED)) {
mm_dbg ("Device doesn't support operating mode setting. Ignoring power update.");
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_error_free (error);
- } else {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ ctx->step = SET_OPERATING_MODE_STEP_LAST;
+ set_operating_mode_context_step (ctx);
+ return;
}
- } else if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't set operating mode: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ set_operating_mode_context_complete_and_free (ctx);
+ return;
}
- if (output)
- qmi_message_dms_set_operating_mode_output_unref (output);
+ if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
+ QmiDmsOperatingMode mode;
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ /* Some new devices, like the Dell DW5770, will return an internal error when
+ * trying to bring the power mode to online. We can avoid this by sending the
+ * magic "DMS Set FCC Auth" message before trying. */
+ if (ctx->step == SET_OPERATING_MODE_STEP_FIRST &&
+ qmi_message_dms_set_operating_mode_input_get_mode (ctx->input, &mode, NULL) &&
+ mode == QMI_DMS_OPERATING_MODE_ONLINE &&
+ g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERNAL)) {
+ g_error_free (error);
+ /* Go on to FCC auth */
+ ctx->step++;
+ set_operating_mode_context_step (ctx);
+ qmi_message_dms_set_operating_mode_output_unref (output);
+ return;
+ }
+
+ g_prefix_error (&error, "Couldn't set operating mode: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ qmi_message_dms_set_operating_mode_output_unref (output);
+ set_operating_mode_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Good! we're done, go to last step */
+ ctx->step = SET_OPERATING_MODE_STEP_LAST;
+ set_operating_mode_context_step (ctx);
+}
+
+static void
+set_operating_mode_context_step (SetOperatingModeContext *ctx)
+{
+ switch (ctx->step) {
+ case SET_OPERATING_MODE_STEP_FIRST:
+ mm_dbg ("Setting device operating mode...");
+ qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (ctx->client),
+ ctx->input,
+ 20,
+ NULL,
+ (GAsyncReadyCallback)dms_set_operating_mode_ready,
+ ctx);
+ return;
+ case SET_OPERATING_MODE_STEP_FCC_AUTH:
+ mm_dbg ("Setting FCC auth...");
+ qmi_client_dms_set_fcc_authentication (QMI_CLIENT_DMS (ctx->client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_set_fcc_authentication_ready,
+ ctx);
+ return;
+ case SET_OPERATING_MODE_STEP_RETRY:
+ mm_dbg ("Setting device operating mode (retry)...");
+ qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (ctx->client),
+ ctx->input,
+ 20,
+ NULL,
+ (GAsyncReadyCallback)dms_set_operating_mode_ready,
+ ctx);
+ return;
+ case SET_OPERATING_MODE_STEP_LAST:
+ /* Good! */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ set_operating_mode_context_complete_and_free (ctx);
+ return;
+ default:
+ g_assert_not_reached ();
+ }
}
static void
@@ -2529,41 +2642,27 @@
GAsyncReadyCallback callback,
gpointer user_data)
{
- QmiMessageDmsSetOperatingModeInput *input;
- GSimpleAsyncResult *result;
+ SetOperatingModeContext *ctx;
QmiClient *client = NULL;
- GError *error = NULL;
if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
QMI_SERVICE_DMS, &client,
callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_power_up_down_off);
+ /* Setup context */
+ ctx = g_slice_new0 (SetOperatingModeContext);
+ ctx->self = g_object_ref (self);
+ ctx->client = g_object_ref (client);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ common_power_up_down_off);
+ ctx->input = qmi_message_dms_set_operating_mode_input_new ();
+ qmi_message_dms_set_operating_mode_input_set_mode (ctx->input, mode, NULL);
+ ctx->step = SET_OPERATING_MODE_STEP_FIRST;
- input = qmi_message_dms_set_operating_mode_input_new ();
- if (!qmi_message_dms_set_operating_mode_input_set_mode (
- input,
- mode,
- &error)) {
- qmi_message_dms_set_operating_mode_input_unref (input);
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
-
- mm_dbg ("Setting device operating mode...");
- qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (client),
- input,
- 20,
- NULL,
- (GAsyncReadyCallback)dms_set_operating_mode_ready,
- result);
- qmi_message_dms_set_operating_mode_input_unref (input);
+ set_operating_mode_context_step (ctx);
}
static void
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 17c128c..feebbc6 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -7511,6 +7511,96 @@
}
/*****************************************************************************/
+/* Load network time (Time interface) */
+
+static gchar *
+modem_time_load_network_time_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ gchar *result = NULL;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (response)
+ mm_parse_cclk_response (response, &result, NULL, error);
+ return result;
+}
+
+static void
+modem_time_load_network_time (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CCLK?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load network timezone (Time interface) */
+
+static MMNetworkTimezone *
+modem_time_load_network_timezone_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ MMNetworkTimezone *tz = NULL;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+ if (response)
+ mm_parse_cclk_response (response, NULL, &tz, error);
+ return tz;
+}
+
+static void
+modem_time_load_network_timezone (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CCLK?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Check support (Time interface) */
+
+static const MMBaseModemAtCommand time_check_sequence[] = {
+ { "+CTZU=1", 3, TRUE, mm_base_modem_response_processor_no_result_continue },
+ { "+CCLK?", 3, TRUE, mm_base_modem_response_processor_string },
+ { NULL }
+};
+
+static gboolean
+modem_time_check_support_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
+}
+
+static void
+modem_time_check_support (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_sequence (MM_BASE_MODEM (self),
+ time_check_sequence,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
static const gchar *primary_init_sequence[] = {
/* Ensure echo is off */
@@ -9824,6 +9914,12 @@
static void
iface_modem_time_init (MMIfaceModemTime *iface)
{
+ iface->check_support = modem_time_check_support;
+ iface->check_support_finish = modem_time_check_support_finish;
+ iface->load_network_time = modem_time_load_network_time;
+ iface->load_network_time_finish = modem_time_load_network_time_finish;
+ iface->load_network_timezone = modem_time_load_network_timezone;
+ iface->load_network_timezone_finish = modem_time_load_network_timezone_finish;
}
static void
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index 919954c..199b880 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -130,24 +130,34 @@
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return ctx->cs;
-
if (ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return ctx->ps;
-
if (ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
return ctx->eps;
if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
return ctx->cs;
-
if (ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
return ctx->ps;
-
if (ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
return ctx->eps;
+ /* If one state is DENIED and the others are UNKNOWN, use DENIED */
+ if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED &&
+ ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
+ ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN)
+ return ctx->cs;
+ if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
+ ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED &&
+ ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN)
+ return ctx->ps;
+ if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
+ ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN &&
+ ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED)
+ return ctx->eps;
+
return ctx->cs;
}
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 7c31f9a..0ed204c 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -3265,22 +3265,6 @@
return;
}
- /* Don't allow trying to recover from a power off */
- if (ctx->previous_real_power_state == MM_MODEM_POWER_STATE_OFF) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot recover from a power off");
- set_power_state_context_complete_and_free (ctx);
- return;
- }
-
- /* Supported transitions:
- * UNKNOWN|LOW --> ON
- * ON --> LOW
- * ON|LOW --> OFF
- */
-
/* Fully powering off the modem? */
if (ctx->power_state == MM_MODEM_POWER_STATE_OFF) {
/* Error if unsupported */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index d40082d..9f0feea 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -2621,3 +2621,78 @@
return success;
}
+
+/*****************************************************************************/
+/* +CCLK response parser */
+
+gboolean
+mm_parse_cclk_response (const char *response,
+ gchar **iso8601p,
+ MMNetworkTimezone **tzp,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+ guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+ gint tz = 0;
+ gboolean ret = FALSE;
+
+ g_assert (iso8601p || tzp); /* at least one */
+
+ /* Sample reply: +CCLK: "15/03/05,14:14:26-32" */
+ r = g_regex_new ("[+]CCLK: \"(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d+)([-+]\\d+)\"", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse +CCLK results: ");
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match +CCLK reply");
+ }
+ } else {
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ g_assert (g_match_info_get_match_count (match_info) >= 8);
+
+ if (mm_get_uint_from_match_info (match_info, 1, &year) &&
+ mm_get_uint_from_match_info (match_info, 2, &month) &&
+ mm_get_uint_from_match_info (match_info, 3, &day) &&
+ mm_get_uint_from_match_info (match_info, 4, &hour) &&
+ mm_get_uint_from_match_info (match_info, 5, &minute) &&
+ mm_get_uint_from_match_info (match_info, 6, &second) &&
+ mm_get_int_from_match_info (match_info, 7, &tz)) {
+ /* adjust year */
+ year += 2000;
+ /*
+ * tz = timezone offset in 15 minute intervals
+ */
+ if (iso8601p) {
+ /* Return ISO-8601 format date/time string */
+ *iso8601p = mm_new_iso8601_time (year, month, day, hour,
+ minute, second,
+ TRUE, (tz * 15));
+ }
+ if (tzp) {
+ *tzp = mm_network_timezone_new ();
+ mm_network_timezone_set_offset (*tzp, tz * 15);
+ }
+
+ ret = TRUE;
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse +CCLK reply");
+ }
+ }
+
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ return ret;
+}
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 0ec59af..2e5d8e4 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -20,6 +20,9 @@
#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
#include "glib-object.h"
#include "mm-charsets.h"
@@ -246,4 +249,10 @@
gchar **out_meid,
gchar **out_esn);
+/* +CCLK response parser */
+gboolean mm_parse_cclk_response (const gchar *response,
+ gchar **iso8601p,
+ MMNetworkTimezone **tzp,
+ GError **error);
+
#endif /* MM_MODEM_HELPERS_H */
diff --git a/src/mm-plugin-manager.c b/src/mm-plugin-manager.c
index 73aa749..567555b 100644
--- a/src/mm-plugin-manager.c
+++ b/src/mm-plugin-manager.c
@@ -32,8 +32,12 @@
/* Default time to defer probing checks */
#define DEFER_TIMEOUT_SECS 3
-/* Time to wait for other ports to appear once the first port is exposed */
-#define MIN_PROBING_TIME_SECS 2
+/* Time to wait for ports to appear before starting to probe the first one */
+#define MIN_WAIT_TIME_MSECS 1500
+
+/* Time to wait for other ports to appear once the first port is exposed
+ * (needs to be > MIN_WAIT_TIME_MSECS!!) */
+#define MIN_PROBING_TIME_MSECS 2500
static void initable_iface_init (GInitableIface *iface);
@@ -93,6 +97,7 @@
gulong grabbed_id;
gulong released_id;
+ gboolean min_wait_timeout_expired;
GList *running_probes;
} FindDeviceSupportContext;
@@ -100,6 +105,8 @@
FindDeviceSupportContext *parent_ctx;
GUdevDevice *port;
+ guint initial_timeout_id;
+
GList *plugins;
GList *current;
MMPlugin *best_plugin;
@@ -648,24 +655,18 @@
}
static void
-device_port_grabbed_cb (MMDevice *device,
- GUdevDevice *port,
- FindDeviceSupportContext *ctx)
+port_probe_context_start (FindDeviceSupportContext *ctx,
+ PortProbeContext *port_probe_ctx)
{
- PortProbeContext *port_probe_ctx;
-
- /* Launch probing task on this port with the first plugin of the list */
- port_probe_ctx = g_slice_new0 (PortProbeContext);
- port_probe_ctx->parent_ctx = ctx;
- port_probe_ctx->port = g_object_ref (port);
-
- /* Setup plugins to probe and first one to check */
- port_probe_ctx->plugins = build_plugins_list (ctx->self, device, port);
+ /* Setup plugins to probe and first one to check.
+ * Make sure this plugins list is built after the MIN WAIT TIME has been expired
+ * (so that per-driver filters work correctly) */
+ port_probe_ctx->plugins = build_plugins_list (ctx->self, ctx->device, port_probe_ctx->port);
port_probe_ctx->current = port_probe_ctx->plugins;
/* If we got one suggested, it will be the first one, unless it is the generic plugin */
- port_probe_ctx->suggested_plugin = (!!mm_device_peek_plugin (device) ?
- MM_PLUGIN (mm_device_get_plugin (device)) :
+ port_probe_ctx->suggested_plugin = (!!mm_device_peek_plugin (ctx->device) ?
+ MM_PLUGIN (mm_device_get_plugin (ctx->device)) :
NULL);
if (port_probe_ctx->suggested_plugin) {
if (g_str_equal (mm_plugin_get_name (port_probe_ctx->suggested_plugin),
@@ -677,11 +678,27 @@
port_probe_ctx->suggested_plugin);
}
- /* Set as running */
+ port_probe_context_step (port_probe_ctx);
+}
+
+static void
+device_port_grabbed_cb (MMDevice *device,
+ GUdevDevice *port,
+ FindDeviceSupportContext *ctx)
+{
+ PortProbeContext *port_probe_ctx;
+
+ /* Launch probing task on this port with the first plugin of the list */
+ port_probe_ctx = g_slice_new0 (PortProbeContext);
+ port_probe_ctx->parent_ctx = ctx;
+ port_probe_ctx->port = g_object_ref (port);
+
+ /* Schedule in the list of probes to run */
ctx->running_probes = g_list_prepend (ctx->running_probes, port_probe_ctx);
- /* Launch supports check in the Plugin Manager */
- port_probe_context_step (port_probe_ctx);
+ /* If port grabbed after the min wait timeout expired, launch probing directly */
+ if (ctx->min_wait_timeout_expired)
+ port_probe_context_start (ctx, port_probe_ctx);
}
static void
@@ -727,6 +744,30 @@
return FALSE;
}
+static gboolean
+min_wait_timeout_cb (FindDeviceSupportContext *ctx)
+{
+ GList *l;
+
+ ctx->min_wait_timeout_expired = TRUE;
+
+ /* Launch supports check for each port in the Plugin Manager */
+ for (l = ctx->running_probes; l; l = g_list_next (l))
+ port_probe_context_start (ctx, (PortProbeContext *)(l->data));
+
+ /* Schedule additional min probing timeout */
+
+ /* Set the initial timeout of 2s. We force the probing time of the device to
+ * be at least this amount of time, so that the kernel has enough time to
+ * bring up ports. Given that we launch this only when the first port of the
+ * device has been exposed in udev, this timeout effectively means that we
+ * leave up to 2s to the remaining ports to appear. */
+ ctx->timeout_id = g_timeout_add (MIN_PROBING_TIME_MSECS - MIN_WAIT_TIME_MSECS,
+ (GSourceFunc)min_probing_timeout_cb,
+ ctx);
+ return FALSE;
+}
+
void
mm_plugin_manager_find_device_support (MMPluginManager *self,
MMDevice *device,
@@ -756,15 +797,15 @@
G_CALLBACK (device_port_released_cb),
ctx);
- /* Set the initial timeout of 2s. We force the probing time of the device to
- * be at least this amount of time, so that the kernel has enough time to
- * bring up ports. Given that we launch this only when the first port of the
- * device has been exposed in udev, this timeout effectively means that we
- * leave up to 2s to the remaining ports to appear. */
ctx->timer = g_timer_new ();
- ctx->timeout_id = g_timeout_add_seconds (MIN_PROBING_TIME_SECS,
- (GSourceFunc)min_probing_timeout_cb,
- ctx);
+
+ /* Set the initial waiting timeout. We don't want to probe any port before
+ * this timeout expires, so that we get as many ports added in the device
+ * as possible. If we don't do this, some plugin filters won't work properly,
+ * like the 'forbidden-drivers' one */
+ ctx->timeout_id = g_timeout_add (MIN_WAIT_TIME_MSECS,
+ (GSourceFunc)min_wait_timeout_cb,
+ ctx);
}
/*****************************************************************************/
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index a83baea..7a3986c 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -214,9 +214,20 @@
}
/* The plugin may specify that only some drivers are supported, or that some
- * drivers are not supported. If that is the case, filter by driver */
+ * drivers are not supported. If that is the case, filter by driver.
+ *
+ * The QMI and MBIM *forbidden* drivers filter is implicit. This is, if the
+ * plugin doesn't explicitly specify that QMI is allowed and we find a QMI
+ * port, the plugin will filter the device. Same for MBIM.
+ *
+ * The opposite, though, is not applicable. If the plugin specifies that QMI
+ * is allowed, we won't take that as a mandatory requirement to look for the
+ * QMI driver (as the plugin may handle non-QMI modems as well)
+ */
if (self->priv->drivers ||
- self->priv->forbidden_drivers) {
+ self->priv->forbidden_drivers ||
+ !self->priv->qmi ||
+ !self->priv->mbim) {
static const gchar *virtual_drivers [] = { "virtual", NULL };
const gchar **drivers;
@@ -254,8 +265,9 @@
return TRUE;
}
}
+
/* Filtering by forbidden drivers */
- else {
+ if (self->priv->forbidden_drivers) {
for (i = 0; self->priv->forbidden_drivers[i]; i++) {
guint j;
@@ -270,6 +282,36 @@
}
}
}
+
+ /* Implicit filter for forbidden QMI driver */
+ if (!self->priv->qmi) {
+ guint j;
+
+ for (j = 0; drivers[j]; j++) {
+ /* If we match the QMI driver: unsupported */
+ if (g_str_equal (drivers[j], "qmi_wwan")) {
+ mm_dbg ("(%s) [%s] filtered by implicit QMI driver",
+ self->priv->name,
+ g_udev_device_get_name (port));
+ return TRUE;
+ }
+ }
+ }
+
+ /* Implicit filter for forbidden MBIM driver */
+ if (!self->priv->mbim) {
+ guint j;
+
+ for (j = 0; drivers[j]; j++) {
+ /* If we match the MBIM driver: unsupported */
+ if (g_str_equal (drivers[j], "cdc_mbim")) {
+ mm_dbg ("(%s) [%s] filtered by implicit MBIM driver",
+ self->priv->name,
+ g_udev_device_get_name (port));
+ return TRUE;
+ }
+ }
+ }
}
vendor = mm_device_get_vendor (device);
@@ -308,6 +350,14 @@
if (!self->priv->product_ids[i].l)
product_filtered = TRUE;
}
+
+ /* When both vendor ids and product ids are given, it may be the case that
+ * we're allowing a full VID1 and only a subset of another VID2, so try to
+ * handle that properly. */
+ if (vendor_filtered && !product_filtered)
+ vendor_filtered = FALSE;
+ if (product_filtered && self->priv->vendor_ids && !vendor_filtered)
+ product_filtered = FALSE;
}
/* If we got filtered by vendor or product IDs; mark it as unsupported only if:
diff --git a/src/mm-sleep-monitor-systemd.c b/src/mm-sleep-monitor-systemd.c
new file mode 100644
index 0000000..c5aae5a
--- /dev/null
+++ b/src/mm-sleep-monitor-systemd.c
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2012 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
+#include "mm-log.h"
+#include "mm-utils.h"
+#include "mm-sleep-monitor.h"
+
+#define SD_NAME "org.freedesktop.login1"
+#define SD_PATH "/org/freedesktop/login1"
+#define SD_INTERFACE "org.freedesktop.login1.Manager"
+
+
+struct _MMSleepMonitor {
+ GObject parent_instance;
+
+ GDBusProxy *sd_proxy;
+ gint inhibit_fd;
+};
+
+struct _MMSleepMonitorClass {
+ GObjectClass parent_class;
+
+ void (*sleeping) (MMSleepMonitor *monitor);
+ void (*resuming) (MMSleepMonitor *monitor);
+};
+
+
+enum {
+ SLEEPING,
+ RESUMING,
+ LAST_SIGNAL,
+};
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE (MMSleepMonitor, mm_sleep_monitor, G_TYPE_OBJECT);
+
+/********************************************************************/
+
+static gboolean
+drop_inhibitor (MMSleepMonitor *self)
+{
+ if (self->inhibit_fd >= 0) {
+ mm_dbg ("[sleep-monitor] dropping systemd sleep inhibitor");
+ close (self->inhibit_fd);
+ self->inhibit_fd = -1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+inhibit_done (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusProxy *sd_proxy = G_DBUS_PROXY (source);
+ MMSleepMonitor *self = user_data;
+ GError *error = NULL;
+ GVariant *res;
+ GUnixFDList *fd_list;
+
+ res = g_dbus_proxy_call_with_unix_fd_list_finish (sd_proxy, &fd_list, result, &error);
+ if (!res) {
+ mm_warn ("[sleep-monitor] inhibit failed: %s", error->message);
+ g_error_free (error);
+ } else {
+ if (!fd_list || g_unix_fd_list_get_length (fd_list) != 1)
+ mm_warn ("[sleep-monitor] didn't get a single fd back");
+
+ self->inhibit_fd = g_unix_fd_list_get (fd_list, 0, NULL);
+
+ mm_dbg ("[sleep-monitor] inhibitor fd is %d", self->inhibit_fd);
+ g_object_unref (fd_list);
+ g_variant_unref (res);
+ }
+}
+
+static void
+take_inhibitor (MMSleepMonitor *self)
+{
+ g_assert (self->inhibit_fd == -1);
+
+ mm_dbg ("[sleep-monitor] taking systemd sleep inhibitor");
+ g_dbus_proxy_call_with_unix_fd_list (self->sd_proxy,
+ "Inhibit",
+ g_variant_new ("(ssss)",
+ "sleep",
+ "ModemManager",
+ _("ModemManager needs to reset devices"),
+ "delay"),
+ 0,
+ G_MAXINT,
+ NULL,
+ NULL,
+ inhibit_done,
+ self);
+}
+
+static void
+signal_cb (GDBusProxy *proxy,
+ const gchar *sendername,
+ const gchar *signalname,
+ GVariant *args,
+ gpointer data)
+{
+ MMSleepMonitor *self = data;
+ gboolean is_about_to_suspend;
+
+ if (strcmp (signalname, "PrepareForSleep") != 0)
+ return;
+
+ g_variant_get (args, "(b)", &is_about_to_suspend);
+ mm_dbg ("[sleep-monitor] received PrepareForSleep signal: %d", is_about_to_suspend);
+
+ if (is_about_to_suspend) {
+ g_signal_emit (self, signals[SLEEPING], 0);
+ drop_inhibitor (self);
+ } else {
+ take_inhibitor (self);
+ g_signal_emit (self, signals[RESUMING], 0);
+ }
+}
+
+static void
+name_owner_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GDBusProxy *proxy = G_DBUS_PROXY (object);
+ MMSleepMonitor *self = MM_SLEEP_MONITOR (user_data);
+ char *owner;
+
+ g_assert (proxy == self->sd_proxy);
+
+ owner = g_dbus_proxy_get_name_owner (proxy);
+ if (owner)
+ take_inhibitor (self);
+ else
+ drop_inhibitor (self);
+ g_free (owner);
+}
+
+static void
+on_proxy_acquired (GObject *object,
+ GAsyncResult *res,
+ MMSleepMonitor *self)
+{
+ GError *error = NULL;
+ char *owner;
+
+ self->sd_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (!self->sd_proxy) {
+ mm_warn ("[sleep-monitor] failed to acquire logind proxy: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_signal_connect (self->sd_proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self);
+ g_signal_connect (self->sd_proxy, "g-signal", G_CALLBACK (signal_cb), self);
+
+ owner = g_dbus_proxy_get_name_owner (self->sd_proxy);
+ if (owner)
+ take_inhibitor (self);
+ g_free (owner);
+}
+
+static void
+mm_sleep_monitor_init (MMSleepMonitor *self)
+{
+ self->inhibit_fd = -1;
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ SD_NAME, SD_PATH, SD_INTERFACE,
+ NULL,
+ (GAsyncReadyCallback) on_proxy_acquired, self);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
+
+ drop_inhibitor (self);
+ if (self->sd_proxy)
+ g_object_unref (self->sd_proxy);
+
+ if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
+}
+
+static void
+mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = finalize;
+
+ signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
+ MM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
+ MM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MMSleepMonitorClass, resuming),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+MM_DEFINE_SINGLETON_GETTER (MMSleepMonitor, mm_sleep_monitor_get, MM_TYPE_SLEEP_MONITOR);
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/mm-sleep-monitor-upower.c b/src/mm-sleep-monitor-upower.c
new file mode 100644
index 0000000..237e3c4
--- /dev/null
+++ b/src/mm-sleep-monitor-upower.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2012 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ *
+ * Port to GDBus:
+ * (C) Copyright 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <gio/gio.h>
+
+#include "mm-log.h"
+#include "mm-utils.h"
+#include "mm-sleep-monitor.h"
+
+#define UPOWER_NAME "org.freedesktop.UPower"
+#define UPOWER_PATH "/org/freedesktop/UPower"
+#define UPOWER_INTERFACE "org.freedesktop.UPower"
+
+struct _MMSleepMonitor {
+ GObject parent_instance;
+
+ GDBusProxy *upower_proxy;
+};
+
+struct _MMSleepMonitorClass {
+ GObjectClass parent_class;
+
+ void (*sleeping) (MMSleepMonitor *monitor);
+ void (*resuming) (MMSleepMonitor *monitor);
+};
+
+
+enum {
+ SLEEPING,
+ RESUMING,
+ LAST_SIGNAL,
+};
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE (MMSleepMonitor, mm_sleep_monitor, G_TYPE_OBJECT);
+
+/********************************************************************/
+
+static void
+signal_cb (GDBusProxy *proxy,
+ const gchar *sendername,
+ const gchar *signalname,
+ GVariant *args,
+ gpointer data)
+{
+ MMSleepMonitor *self = data;
+
+ if (strcmp (signalname, "Sleeping") == 0) {
+ mm_dbg ("[sleep-monitor] received UPower sleeping signal");
+ g_signal_emit (self, signals[SLEEPING], 0);
+ } else if (strcmp (signalname, "Resuming") == 0) {
+ mm_dbg ("[sleep-monitor] received UPower resuming signal");
+ g_signal_emit (self, signals[RESUMING], 0);
+ }
+}
+
+static void
+on_proxy_acquired (GObject *object,
+ GAsyncResult *res,
+ MMSleepMonitor *self)
+{
+ GError *error = NULL;
+
+ self->upower_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (!self->upower_proxy) {
+ mm_warn ("[sleep-monitor] failed to acquire UPower proxy: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_signal_connect (self->upower_proxy, "g-signal", G_CALLBACK (signal_cb), self);
+}
+
+static void
+mm_sleep_monitor_init (MMSleepMonitor *self)
+{
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ UPOWER_NAME, UPOWER_PATH, UPOWER_INTERFACE,
+ NULL,
+ (GAsyncReadyCallback) on_proxy_acquired, self);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
+
+ if (self->upower_proxy)
+ g_object_unref (self->upower_proxy);
+
+ if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
+}
+
+static void
+mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = finalize;
+
+ signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
+ MM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
+ MM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MMSleepMonitorClass, resuming),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+MM_DEFINE_SINGLETON_GETTER (MMSleepMonitor, mm_sleep_monitor_get, MM_TYPE_SLEEP_MONITOR);
diff --git a/src/mm-sleep-monitor.h b/src/mm-sleep-monitor.h
new file mode 100644
index 0000000..2fac551
--- /dev/null
+++ b/src/mm-sleep-monitor.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * (C) Copyright 2012 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ * Original code imported from NetworkManager.
+ */
+
+#ifndef __MM_SLEEP_MONITOR_H__
+#define __MM_SLEEP_MONITOR_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_SLEEP_MONITOR (mm_sleep_monitor_get_type ())
+#define MM_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MM_TYPE_SLEEP_MONITOR, MMSleepMonitor))
+#define MM_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MM_TYPE_SLEEP_MONITOR, MMSleepMonitorClass))
+#define MM_SLEEP_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MM_TYPE_SLEEP_MONITOR, MMSleepMonitorClass))
+#define MM_IS_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MM_TYPE_SLEEP_MONITOR))
+#define MM_IS_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MM_TYPE_SLEEP_MONITOR))
+
+#define MM_SLEEP_MONITOR_SLEEPING "sleeping"
+#define MM_SLEEP_MONITOR_RESUMING "resuming"
+
+typedef struct _MMSleepMonitor MMSleepMonitor;
+typedef struct _MMSleepMonitorClass MMSleepMonitorClass;
+
+GType mm_sleep_monitor_get_type (void) G_GNUC_CONST;
+MMSleepMonitor *mm_sleep_monitor_get (void);
+
+G_END_DECLS
+
+#endif /* __MM_SLEEP_MONITOR_H__ */
diff --git a/src/mm-utils.h b/src/mm-utils.h
new file mode 100644
index 0000000..2b0a934
--- /dev/null
+++ b/src/mm-utils.h
@@ -0,0 +1,83 @@
+/* -*- 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:
+ *
+ * Singleton support imported from NetworkManager.
+ * (C) Copyright 2014 Red Hat, Inc.
+ */
+
+#ifndef MM_UTILS_H
+#define MM_UTILS_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-log.h"
+
+/*****************************************************************************/
+
+#define MM_DEFINE_SINGLETON_INSTANCE(TYPE) \
+ static TYPE *singleton_instance
+
+#define MM_DEFINE_SINGLETON_WEAK_REF(TYPE) \
+ MM_DEFINE_SINGLETON_INSTANCE (TYPE); \
+ static void \
+ _singleton_instance_weak_ref_cb (gpointer data, \
+ GObject *where_the_object_was) \
+ { \
+ mm_dbg ("disposing %s singleton (%p)", G_STRINGIFY (TYPE), singleton_instance); \
+ singleton_instance = NULL; \
+ } \
+ static inline void \
+ mm_singleton_instance_weak_ref_register (void) \
+ { \
+ g_object_weak_ref (G_OBJECT (singleton_instance), _singleton_instance_weak_ref_cb, NULL); \
+ }
+
+#define MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE) \
+ MM_DEFINE_SINGLETON_INSTANCE (TYPE); \
+ static void __attribute__((destructor)) \
+ _singleton_destructor (void) \
+ { \
+ if (singleton_instance) { \
+ if (G_OBJECT (singleton_instance)->ref_count > 1) \
+ mm_dbg ("disown %s singleton (%p)", G_STRINGIFY (TYPE), singleton_instance); \
+ g_object_unref (singleton_instance); \
+ } \
+ }
+
+/* By default, the getter will assert that the singleton will be created only once. You can
+ * change this by redefining MM_DEFINE_SINGLETON_ALLOW_MULTIPLE. */
+#ifndef MM_DEFINE_SINGLETON_ALLOW_MULTIPLE
+#define MM_DEFINE_SINGLETON_ALLOW_MULTIPLE FALSE
+#endif
+
+#define MM_DEFINE_SINGLETON_GETTER(TYPE, GETTER, GTYPE, ...) \
+ MM_DEFINE_SINGLETON_INSTANCE (TYPE); \
+ MM_DEFINE_SINGLETON_WEAK_REF (TYPE); \
+ TYPE * \
+ GETTER (void) \
+ { \
+ if (G_UNLIKELY (!singleton_instance)) { \
+ static char _already_created = FALSE; \
+ \
+ g_assert (!_already_created || (MM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \
+ _already_created = TRUE; \
+ singleton_instance = (g_object_new (GTYPE, ##__VA_ARGS__, NULL)); \
+ g_assert (singleton_instance); \
+ mm_singleton_instance_weak_ref_register (); \
+ mm_dbg ("create %s singleton (%p)", G_STRINGIFY (TYPE), singleton_instance); \
+ } \
+ return singleton_instance; \
+ } \
+ MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE)
+
+#endif /* MM_UTILS_H */
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index b493e6b..9fd2501 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -2341,6 +2341,77 @@
}
/*****************************************************************************/
+/* Test +CCLK responses */
+
+typedef struct {
+ const gchar *str;
+ gboolean ret;
+ gboolean test_iso8601;
+ gboolean test_tz;
+ gchar *iso8601;
+ gint32 offset;
+} CclkTest;
+
+static const CclkTest cclk_tests[] = {
+ { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, FALSE,
+ "2014-08-05T04:00:21+10:00", 600 },
+ { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, FALSE, TRUE,
+ "2014-08-05T04:00:21+10:00", 600 },
+ { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, TRUE,
+ "2014-08-05T04:00:21+10:00", 600 },
+
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE,
+ "2015-02-28T20:30:40-08:00", -480 },
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE,
+ "2015-02-28T20:30:40-08:00", -480 },
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE,
+ "2015-02-28T20:30:40-08:00", -480 },
+
+ { "+CCLK: \"XX/XX/XX,XX:XX:XX+XX\"", FALSE, TRUE, FALSE,
+ NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN },
+
+ { NULL, FALSE, FALSE, FALSE, NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN }
+};
+
+static void
+test_cclk_response (void)
+{
+ guint i;
+
+ for (i = 0; cclk_tests[i].str; i++) {
+ GError *error = NULL;
+ gchar *iso8601 = NULL;
+ MMNetworkTimezone *tz = NULL;
+ gboolean ret;
+
+ ret = mm_parse_cclk_response (cclk_tests[i].str,
+ cclk_tests[i].test_iso8601 ? &iso8601 : NULL,
+ cclk_tests[i].test_tz ? &tz : NULL,
+ &error);
+
+ g_assert (ret == cclk_tests[i].ret);
+ g_assert (ret == (error ? FALSE : TRUE));
+
+ g_clear_error (&error);
+
+ if (cclk_tests[i].test_iso8601)
+ g_assert_cmpstr (cclk_tests[i].iso8601, ==, iso8601);
+
+ if (cclk_tests[i].test_tz) {
+ g_assert (mm_network_timezone_get_offset (tz) == cclk_tests[i].offset);
+ g_assert (mm_network_timezone_get_dst_offset (tz) == MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN);
+ g_assert (mm_network_timezone_get_leap_seconds (tz) == MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN);
+ }
+
+ if (iso8601)
+ g_free (iso8601);
+
+ if (tz)
+ g_object_unref (tz);
+ }
+}
+
+/*****************************************************************************/
void
_mm_log (const char *loc,
@@ -2501,6 +2572,8 @@
g_test_suite_add (suite, TESTCASE (test_supported_capability_filter, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cclk_response, NULL));
+
result = g_test_run ();
reg_test_data_free (reg_data);
diff --git a/test/Makefile.am b/test/Makefile.am
index f0bfd18..6e6779a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,9 +1,24 @@
-noinst_PROGRAMS = lsudev
+noinst_PROGRAMS = lsudev mmtty
lsudev_SOURCES = lsudev.c
lsudev_CPPFLAGS = $(GUDEV_CFLAGS)
lsudev_LDADD = $(GUDEV_LIBS)
+mmtty_SOURCES = mmtty.c
+mmtty_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ -I$(top_srcdir)/libmm-glib \
+ -I$(top_srcdir)/libmm-glib/generated \
+ -I$(top_builddir)/libmm-glib/generated
+mmtty_LDADD = \
+ $(MM_LIBS) \
+ $(top_builddir)/src/libport.la \
+ $(top_builddir)/src/libmodem-helpers.la
+
EXTRA_DIST = \
mmcli-test-sms
diff --git a/test/mmtty.c b/test/mmtty.c
new file mode 100644
index 0000000..677203a
--- /dev/null
+++ b/test/mmtty.c
@@ -0,0 +1,299 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <mm-log.h>
+#include <mm-port-serial.h>
+#include <mm-port-serial-at.h>
+#include <mm-serial-parsers.h>
+
+#define PROGRAM_NAME "mmtty"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+/* Globals */
+static MMPortSerialAt *port;
+static GMainLoop *loop;
+static GIOChannel *input;
+static guint input_watch_id;
+
+/* Context */
+static gchar *device_str;
+static gboolean no_flash_flag;
+static gboolean no_echo_removal_flag;
+static gint64 send_delay = -1;
+static gboolean verbose_flag;
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+ { "device", 'd', 0, G_OPTION_ARG_STRING, &device_str,
+ "Specify device path",
+ "[PATH]"
+ },
+ { "no-flash", 0, 0, G_OPTION_ARG_NONE, &no_flash_flag,
+ "Avoid flashing the port while opening",
+ NULL
+ },
+ { "no-echo-removal", 0, 0, G_OPTION_ARG_NONE, &no_echo_removal_flag,
+ "Avoid logic to remove echo",
+ NULL
+ },
+ { "send-delay", 0, 0, G_OPTION_ARG_INT64, &send_delay,
+ "Send delay for each byte in microseconds (default=1000)",
+ "[DELAY]"
+ },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
+ "Run action with verbose logs",
+ NULL
+ },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ { NULL }
+};
+
+static void
+signals_handler (int signum)
+{
+ if (loop && g_main_loop_is_running (loop)) {
+ g_printerr ("%s\n",
+ "cancelling the main loop...\n");
+ g_main_loop_quit (loop);
+ }
+}
+
+void
+_mm_log (const char *loc,
+ const char *func,
+ guint32 level,
+ const char *fmt,
+ ...)
+{
+ va_list args;
+ gchar *msg;
+
+ if (!verbose_flag)
+ return;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+}
+
+static void
+print_version_and_exit (void)
+{
+ g_print ("\n"
+ PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (2015) Aleksander Morgado\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n"
+ "\n");
+ exit (EXIT_SUCCESS);
+}
+
+static void
+at_command_ready (MMPortSerialAt *serial_at,
+ GAsyncResult *res)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_port_serial_at_command_finish (serial_at, res, &error);
+ if (response)
+ g_print ("%s\n", response);
+ if (error) {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ }
+
+ g_print ("> ");
+}
+
+static gboolean
+input_callback (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GError *error = NULL;
+ GIOStatus status;
+ gchar *line = NULL;
+
+ status = g_io_channel_read_line (channel, &line, NULL, NULL, &error);
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ mm_port_serial_at_command (port, line, 60, FALSE, FALSE, NULL,
+ (GAsyncReadyCallback) at_command_ready, NULL);
+ g_free (line);
+ return TRUE;
+
+ case G_IO_STATUS_ERROR:
+ g_printerr ("error: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+
+ case G_IO_STATUS_EOF:
+ g_warning ("error: No input data available");
+ return TRUE;
+
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return FALSE;
+}
+
+static void
+flash_ready (MMPortSerial *serial,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ if (!mm_port_serial_flash_finish (serial, res, &error)) {
+ g_printerr ("error: cannot flash serial port: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("ready\n");
+ g_print ("> ");
+
+ /* Setup input reading */
+ input = g_io_channel_unix_new (STDIN_FILENO);
+ input_watch_id = g_io_add_watch (input, G_IO_IN, (GIOFunc) input_callback, NULL);
+}
+
+static void
+serial_buffer_full (void)
+{
+ g_printerr ("error: serial buffer full\n");
+}
+
+static gboolean
+start_cb (void)
+{
+ GError *error = NULL;
+ const gchar *device_name;
+
+ device_name = device_str;
+ if (g_str_has_prefix (device_name, "/dev/"))
+ device_name += strlen ("/dev/");
+
+ g_print ("creating AT capable serial port for device '%s'...\n", device_name);
+ port = mm_port_serial_at_new (device_name, MM_PORT_SUBSYS_TTY);
+
+ /* Setup send delay */
+ if (send_delay >= 0) {
+ if (send_delay > 0)
+ g_print ("updating send delay to %" G_GINT64_FORMAT "us...\n", send_delay);
+ else
+ g_print ("disabling send delay...\n");
+ g_object_set (port, MM_PORT_SERIAL_SEND_DELAY, send_delay, NULL);
+ }
+
+ /* Setup echo removal */
+ if (no_echo_removal_flag) {
+ g_print ("disabling echo removal...\n");
+ g_object_set (port, MM_PORT_SERIAL_AT_REMOVE_ECHO, FALSE, NULL);
+ }
+
+ /* Set common response parser */
+ mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
+
+ /* Try to open the port... */
+ g_print ("opening serial port...\n");
+ if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) {
+ g_printerr ("error: cannot open serial port: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Warn on full buffer by default */
+ g_signal_connect (port, "buffer-full", G_CALLBACK (serial_buffer_full), NULL);
+
+ /* If we set FLASH_OK to FALSE, the flashing operation does nothing */
+ if (no_flash_flag) {
+ g_print ("disabling serial port flash...\n");
+ g_object_set (port, MM_PORT_SERIAL_FLASH_OK, FALSE, NULL);
+ } else
+ g_print ("flashing serial port...\n");
+ mm_port_serial_flash (MM_PORT_SERIAL (port),
+ 100,
+ FALSE,
+ (GAsyncReadyCallback) flash_ready,
+ NULL);
+ return FALSE;
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+
+ setlocale (LC_ALL, "");
+
+ g_type_init ();
+
+ /* Setup option context, process it and destroy it */
+ context = g_option_context_new ("- ModemManager TTY testing");
+ g_option_context_add_main_entries (context, main_entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (version_flag)
+ print_version_and_exit ();
+
+ /* No device path given? */
+ if (!device_str) {
+ g_printerr ("error: no device path specified\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Setup signals */
+ signal (SIGINT, signals_handler);
+ signal (SIGHUP, signals_handler);
+ signal (SIGTERM, signals_handler);
+
+ /* Setup main loop and shedule start in idle */
+ loop = g_main_loop_new (NULL, FALSE);
+ g_idle_add ((GSourceFunc)start_cb, NULL);
+ g_main_loop_run (loop);
+
+ /* Cleanup */
+ g_main_loop_unref (loop);
+ if (port) {
+ if (mm_port_serial_is_open (MM_PORT_SERIAL (port)))
+ mm_port_serial_close (MM_PORT_SERIAL (port));
+ g_object_unref (port);
+ }
+ if (input)
+ g_io_channel_unref (input);
+ return 0;
+}