diff options
| author | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
| commit | 33491bc4279481db8ae47213e34a6d695a0e8830 (patch) | |
| tree | 097d2b0fdff3fae6885381ae5e57a182cd8cbbba /src/devices/wwan | |
| parent | 59c3714a494c3b3765657c0551ad82842d98a7d2 (diff) | |
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'src/devices/wwan')
| -rw-r--r-- | src/devices/wwan/Makefile.am | 95 | ||||
| -rw-r--r-- | src/devices/wwan/Makefile.in | 886 | ||||
| -rw-r--r-- | src/devices/wwan/README | 45 | ||||
| -rw-r--r-- | src/devices/wwan/exports.ver | 7 | ||||
| -rw-r--r-- | src/devices/wwan/nm-device-modem-glue.h | 73 | ||||
| -rw-r--r-- | src/devices/wwan/nm-device-modem.c | 646 | ||||
| -rw-r--r-- | src/devices/wwan/nm-device-modem.h | 54 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-broadband.c | 987 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-broadband.h | 58 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-enum-types.c | 64 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-enum-types.h | 21 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-manager.c | 757 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-manager.h | 49 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-old-types.h | 70 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-old.c | 1139 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-old.h | 53 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem.c | 1119 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem.h | 217 | ||||
| -rw-r--r-- | src/devices/wwan/nm-wwan-factory.c | 136 | ||||
| -rw-r--r-- | src/devices/wwan/nm-wwan-factory.h | 37 | ||||
| -rw-r--r-- | src/devices/wwan/wwan-exports.ver | 28 |
21 files changed, 6541 insertions, 0 deletions
diff --git a/src/devices/wwan/Makefile.am b/src/devices/wwan/Makefile.am new file mode 100644 index 00000000..988f4679 --- /dev/null +++ b/src/devices/wwan/Makefile.am @@ -0,0 +1,95 @@ +include $(GLIB_MAKEFILE) + +@GNOME_CODE_COVERAGE_RULES@ + +AM_CPPFLAGS = \ + -I${top_srcdir}/src \ + -I${top_builddir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src/devices \ + -I${top_srcdir}/src/settings \ + -I${top_srcdir}/src/platform \ + -I${top_builddir}/include \ + -I${top_srcdir}/include \ + -I${top_builddir}/libnm-util \ + -I${top_srcdir}/libnm-util \ + -DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + $(DBUS_CFLAGS) \ + $(POLKIT_CFLAGS) \ + $(MM_GLIB_CFLAGS) + +BUILT_SOURCES = $(null) + +pkglib_LTLIBRARIES = libnm-wwan.la libnm-device-plugin-wwan.la + +########################################################### + +GLIB_GENERATED = nm-modem-enum-types.h nm-modem-enum-types.c +GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM +GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM +nm_modem_enum_types_sources = $(srcdir)/nm-modem.h + +BUILT_SOURCES += $(GLIB_GENERATED) + +libnm_wwan_la_SOURCES = \ + nm-modem-old.c \ + nm-modem-old.h \ + nm-modem-old-types.h \ + nm-modem-manager.c \ + nm-modem-manager.h \ + nm-modem.c \ + nm-modem.h \ + \ + $(GLIB_GENERATED) + +if WITH_MODEM_MANAGER_1 +libnm_wwan_la_SOURCES += \ + nm-modem-broadband.c \ + nm-modem-broadband.h +endif + +WWAN_SYMBOL_VIS_FILE=$(srcdir)/wwan-exports.ver + +libnm_wwan_la_LDFLAGS = \ + -avoid-version \ + -Wl,--version-script=$(WWAN_SYMBOL_VIS_FILE) +libnm_wwan_la_LIBADD = $(DBUS_LIBS) $(MM_GLIB_LIBS) + +########################################################### + +nm-device-modem-glue.h: $(top_srcdir)/introspection/nm-device-modem.xml + dbus-binding-tool --prefix=nm_device_modem --mode=glib-server --output=$@ $< + +BUILT_SOURCES += nm-device-modem-glue.h + +SYMBOL_VIS_FILE=$(srcdir)/exports.ver + +libnm_device_plugin_wwan_la_SOURCES = \ + nm-wwan-factory.c \ + nm-wwan-factory.h \ + nm-device-modem.c \ + nm-device-modem.h \ + nm-device-modem-glue.h + +libnm_device_plugin_wwan_la_LDFLAGS = \ + -module -avoid-version \ + -Wl,--version-script=$(SYMBOL_VIS_FILE) + +libnm_device_plugin_wwan_la_LIBADD = \ + libnm-wwan.la \ + $(DBUS_LIBS) + +########################################################### + +CLEANFILES = $(BUILT_SOURCES) +EXTRA_DIST = $(SYMBOL_VIS_FILE) $(WWAN_SYMBOL_VIS_FILE) + +if ENABLE_TESTS + +check-local: + $(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wwan.so $(SYMBOL_VIS_FILE) + $(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-wwan.so $(WWAN_SYMBOL_VIS_FILE) + +endif + diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in new file mode 100644 index 00000000..ae33d1c8 --- /dev/null +++ b/src/devices/wwan/Makefile.in @@ -0,0 +1,886 @@ +# Makefile.in generated by automake 1.13.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@WITH_MODEM_MANAGER_1_TRUE@am__append_1 = \ +@WITH_MODEM_MANAGER_1_TRUE@ nm-modem-broadband.c \ +@WITH_MODEM_MANAGER_1_TRUE@ nm-modem-broadband.h + +subdir = src/devices/wwan +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/build-aux/depcomp README +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gnome-code-coverage.m4 \ + $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkglibdir)" +LTLIBRARIES = $(pkglib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libnm_device_plugin_wwan_la_DEPENDENCIES = libnm-wwan.la \ + $(am__DEPENDENCIES_1) +am_libnm_device_plugin_wwan_la_OBJECTS = nm-wwan-factory.lo \ + nm-device-modem.lo +libnm_device_plugin_wwan_la_OBJECTS = \ + $(am_libnm_device_plugin_wwan_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnm_device_plugin_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libnm_device_plugin_wwan_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +libnm_wwan_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am__libnm_wwan_la_SOURCES_DIST = nm-modem-old.c nm-modem-old.h \ + nm-modem-old-types.h nm-modem-manager.c nm-modem-manager.h \ + nm-modem.c nm-modem.h nm-modem-enum-types.h \ + nm-modem-enum-types.c nm-modem-broadband.c \ + nm-modem-broadband.h +am__objects_1 = nm-modem-enum-types.lo +@WITH_MODEM_MANAGER_1_TRUE@am__objects_2 = nm-modem-broadband.lo +am_libnm_wwan_la_OBJECTS = nm-modem-old.lo nm-modem-manager.lo \ + nm-modem.lo $(am__objects_1) $(am__objects_2) +libnm_wwan_la_OBJECTS = $(am_libnm_wwan_la_OBJECTS) +libnm_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libnm_wwan_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libnm_device_plugin_wwan_la_SOURCES) \ + $(libnm_wwan_la_SOURCES) +DIST_SOURCES = $(libnm_device_plugin_wwan_la_SOURCES) \ + $(am__libnm_wwan_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CKDB_PATH = @CKDB_PATH@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ +DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ +DLLTOOL = @DLLTOOL@ +DNSMASQ_PATH = @DNSMASQ_PATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MAKEFILE = @GLIB_MAKEFILE@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@ +INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@ +INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@ +INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +IPTABLES_PATH = @IPTABLES_PATH@ +IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ +IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ +KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBNDP_CFLAGS = @LIBNDP_CFLAGS@ +LIBNDP_LIBS = @LIBNDP_LIBS@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ +LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ +LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@ +MM_GLIB_LIBS = @MM_GLIB_LIBS@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEWT_CFLAGS = @NEWT_CFLAGS@ +NEWT_LIBS = @NEWT_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ +NM_MICRO_VERSION = @NM_MICRO_VERSION@ +NM_MINOR_VERSION = @NM_MINOR_VERSION@ +NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@ +NM_VERSION = @NM_VERSION@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PPPD_PATH = @PPPD_PATH@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +PPPOE_PATH = @PPPOE_PATH@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +READLINE_LIBS = @READLINE_LIBS@ +SED = @SED@ +SELINUX_CFLAGS = @SELINUX_CFLAGS@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ +SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ +SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ +SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ +SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VALGRIND_RULES = @VALGRIND_RULES@ +VAPIGEN = @VAPIGEN@ +VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@ +VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +intltool__v_merge_options_ = @intltool__v_merge_options_@ +intltool__v_merge_options_0 = @intltool__v_merge_options_0@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +nmbinary = @nmbinary@ +nmconfdir = @nmconfdir@ +nmdatadir = @nmdatadir@ +nmrundir = @nmrundir@ +nmstatedir = @nmstatedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +with_dhclient = @with_dhclient@ +with_dhcpcd = @with_dhcpcd@ +with_netconfig = @with_netconfig@ +with_resolvconf = @with_resolvconf@ +with_valgrind = @with_valgrind@ +AM_CPPFLAGS = \ + -I${top_srcdir}/src \ + -I${top_builddir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src/devices \ + -I${top_srcdir}/src/settings \ + -I${top_srcdir}/src/platform \ + -I${top_builddir}/include \ + -I${top_srcdir}/include \ + -I${top_builddir}/libnm-util \ + -I${top_srcdir}/libnm-util \ + -DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + $(DBUS_CFLAGS) \ + $(POLKIT_CFLAGS) \ + $(MM_GLIB_CFLAGS) + +BUILT_SOURCES = $(null) $(GLIB_GENERATED) nm-device-modem-glue.h +pkglib_LTLIBRARIES = libnm-wwan.la libnm-device-plugin-wwan.la + +########################################################### +GLIB_GENERATED = nm-modem-enum-types.h nm-modem-enum-types.c +GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM +GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM +nm_modem_enum_types_sources = $(srcdir)/nm-modem.h +libnm_wwan_la_SOURCES = nm-modem-old.c nm-modem-old.h \ + nm-modem-old-types.h nm-modem-manager.c nm-modem-manager.h \ + nm-modem.c nm-modem.h $(GLIB_GENERATED) $(am__append_1) +WWAN_SYMBOL_VIS_FILE = $(srcdir)/wwan-exports.ver +libnm_wwan_la_LDFLAGS = \ + -avoid-version \ + -Wl,--version-script=$(WWAN_SYMBOL_VIS_FILE) + +libnm_wwan_la_LIBADD = $(DBUS_LIBS) $(MM_GLIB_LIBS) +SYMBOL_VIS_FILE = $(srcdir)/exports.ver +libnm_device_plugin_wwan_la_SOURCES = \ + nm-wwan-factory.c \ + nm-wwan-factory.h \ + nm-device-modem.c \ + nm-device-modem.h \ + nm-device-modem-glue.h + +libnm_device_plugin_wwan_la_LDFLAGS = \ + -module -avoid-version \ + -Wl,--version-script=$(SYMBOL_VIS_FILE) + +libnm_device_plugin_wwan_la_LIBADD = \ + libnm-wwan.la \ + $(DBUS_LIBS) + + +########################################################### +CLEANFILES = $(BUILT_SOURCES) +EXTRA_DIST = $(SYMBOL_VIS_FILE) $(WWAN_SYMBOL_VIS_FILE) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wwan/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/devices/wwan/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ + } + +uninstall-pkglibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ + done + +clean-pkglibLTLIBRARIES: + -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) + @list='$(pkglib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnm-device-plugin-wwan.la: $(libnm_device_plugin_wwan_la_OBJECTS) $(libnm_device_plugin_wwan_la_DEPENDENCIES) $(EXTRA_libnm_device_plugin_wwan_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnm_device_plugin_wwan_la_LINK) -rpath $(pkglibdir) $(libnm_device_plugin_wwan_la_OBJECTS) $(libnm_device_plugin_wwan_la_LIBADD) $(LIBS) + +libnm-wwan.la: $(libnm_wwan_la_OBJECTS) $(libnm_wwan_la_DEPENDENCIES) $(EXTRA_libnm_wwan_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnm_wwan_la_LINK) -rpath $(pkglibdir) $(libnm_wwan_la_OBJECTS) $(libnm_wwan_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-modem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-broadband.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-enum-types.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-old.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wwan-factory.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +@ENABLE_TESTS_FALSE@check-local: +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(pkglibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-pkglibLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkglibLTLIBRARIES + +.MAKE: all check check-am install install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \ + clean-generic clean-libtool clean-pkglibLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkglibLTLIBRARIES install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkglibLTLIBRARIES + +include $(GLIB_MAKEFILE) + +@GNOME_CODE_COVERAGE_RULES@ + +########################################################### + +nm-device-modem-glue.h: $(top_srcdir)/introspection/nm-device-modem.xml + dbus-binding-tool --prefix=nm_device_modem --mode=glib-server --output=$@ $< + +@ENABLE_TESTS_TRUE@check-local: +@ENABLE_TESTS_TRUE@ $(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wwan.so $(SYMBOL_VIS_FILE) +@ENABLE_TESTS_TRUE@ $(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-wwan.so $(WWAN_SYMBOL_VIS_FILE) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/devices/wwan/README b/src/devices/wwan/README new file mode 100644 index 00000000..4661c042 --- /dev/null +++ b/src/devices/wwan/README @@ -0,0 +1,45 @@ + +ModemManager integration is organized as follows: + + +Common source +******************************************************************************** + + * nm-modem.[h|c]: + Defines the basic `NMModem' object. The core NetworkManager implementation + will use this interface exclusively, regardless of the real final type of + the modem object. + + * nm-modem-manager.[h|c]: + Defines the `NMModemManager' object, which takes care of listening to + signals from the DBus interface notifying about added or removed modems. + It also takes care of creating proper `NMModem' objects from the + information retrieved from the DBus interface. + + +ModemManager 0.7 integration +******************************************************************************** + + * nm-modem-broadband.[h|c]: + Defines the `NMModemBroadband' object, which is a subclass of `NMModem'. + This object handles both 3GPP and 3GPP2 modems exposed in the new + `ModemManager1' interface. + + +ModemManager 0.4/0.5/0.6 integration +******************************************************************************** + + * nm-modem-old-types.h: + Defines helper types to use with the (old) ModemManager DBus API. + + * nm-modem-old.[h|c]: + Defines the `NMModemGeneric' object. All modem objects based on the old + ModemManager interface are subclasses of this one. + + * nm-modem-gsm.[h|c]: + Defines the `NMModemGsm' object, which is a subclass of `NMModemGeneric'. + This object handles 3GPP-specific (GSM, UMTS, HSPA, LTE) modems. + + * nm-modem-cdma.[h|c]: + Defines the `NMModemCdma' object, which is a subclass of `NMModemGeneric'. + This object handles 3GPP2-specific modems (CDMA, EV-DO). diff --git a/src/devices/wwan/exports.ver b/src/devices/wwan/exports.ver new file mode 100644 index 00000000..d2c45124 --- /dev/null +++ b/src/devices/wwan/exports.ver @@ -0,0 +1,7 @@ +{ +global: + nm_device_factory_create; + nm_device_factory_get_device_type; +local: + *; +}; diff --git a/src/devices/wwan/nm-device-modem-glue.h b/src/devices/wwan/nm-device-modem-glue.h new file mode 100644 index 00000000..21271491 --- /dev/null +++ b/src/devices/wwan/nm-device-modem-glue.h @@ -0,0 +1,73 @@ +/* Generated by dbus-binding-tool; do not edit! */ + + +#ifndef __dbus_glib_marshal_nm_device_modem_MARSHAL_H__ +#define __dbus_glib_marshal_nm_device_modem_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_schar (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +G_END_DECLS + +#endif /* __dbus_glib_marshal_nm_device_modem_MARSHAL_H__ */ + +#include <dbus/dbus-glib.h> +static const DBusGMethodInfo dbus_glib_nm_device_modem_methods[] = { +}; + +const DBusGObjectInfo dbus_glib_nm_device_modem_object_info = { 1, + dbus_glib_nm_device_modem_methods, + 0, +"\0", +"org.freedesktop.NetworkManager.Device.Modem\0PropertiesChanged\0\0", +"org.freedesktop.NetworkManager.Device.Modem\0ModemCapabilities\0modem_capabilities\0read\0org.freedesktop.NetworkManager.Device.Modem\0CurrentCapabilities\0current_capabilities\0read\0\0" +}; + diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c new file mode 100644 index 00000000..678f7795 --- /dev/null +++ b/src/devices/wwan/nm-device-modem.c @@ -0,0 +1,646 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2011 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> + +#include "nm-device-modem.h" +#include "nm-modem.h" +#include "nm-modem-old.h" +#include "nm-device-private.h" +#include "nm-rfkill-manager.h" +#include "nm-logging.h" +#include "nm-dbus-manager.h" +#include "nm-settings-connection.h" + +#if WITH_MODEM_MANAGER_1 +#include "nm-modem-broadband.h" +#endif + +G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) + +#define NM_DEVICE_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MODEM, NMDeviceModemPrivate)) + +#include "nm-device-modem-glue.h" + +typedef struct { + NMModem *modem; + NMDeviceModemCapabilities caps; + NMDeviceModemCapabilities current_caps; + gboolean rf_enabled; +} NMDeviceModemPrivate; + +enum { + PROP_0, + PROP_MODEM, + PROP_CAPABILITIES, + PROP_CURRENT_CAPABILITIES, +}; + +/*****************************************************************************/ + +static void +ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + switch (nm_device_get_state (device)) { + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_NEED_AUTH: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + break; + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + case NM_DEVICE_STATE_SECONDARIES: + case NM_DEVICE_STATE_ACTIVATED: + if (nm_device_activate_ip4_state_in_conf (device)) + nm_device_activate_schedule_ip4_config_timeout (device); + else { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } + break; + default: + break; + } +} + +static void +modem_prepare_result (NMModem *modem, + gboolean success, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceState state; + + state = nm_device_get_state (device); + g_return_if_fail (state == NM_DEVICE_STATE_PREPARE); + + if (success) + nm_device_activate_schedule_stage2_device_config (device); + else { + if (reason == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) { + /* If the connect failed because the SIM PIN was wrong don't allow + * the device to be auto-activated anymore, which would risk locking + * the SIM if the incorrect PIN continues to be used. + */ + g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, FALSE, NULL); + nm_log_info (LOGD_MB, "(%s): disabling autoconnect due to failed SIM PIN", + nm_device_get_iface (device)); + } + + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + } +} + +static void +modem_auth_requested (NMModem *modem, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + /* Auth requests (PIN, PAP/CHAP passwords, etc) only get handled + * during activation. + */ + if (!nm_device_is_activating (device)) + return; + + nm_device_state_changed (device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_NONE); +} + +static void +modem_auth_result (NMModem *modem, GError *error, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + if (error) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } else { + /* Otherwise, on success for modem secrets we need to schedule stage1 again */ + g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); + nm_device_activate_schedule_stage1_device_prepare (device); + } +} + +static void +modem_ip4_config_result (NMModem *self, + NMIP4Config *config, + GError *error, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE); + + if (error) { + nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IP4 configuration failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } else + nm_device_activate_schedule_ip4_config_result (device, config); +} + +static void +data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + /* We set the IP iface in the device as soon as we know it, so that we + * properly ifup it if needed */ + nm_device_set_ip_iface (self, nm_modem_get_data_port (modem)); +} + +static void +modem_state_cb (NMModem *modem, + NMModemState new_state, + NMModemState old_state, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + NMDeviceState dev_state = nm_device_get_state (device); + + if (new_state <= NM_MODEM_STATE_DISABLING && + old_state > NM_MODEM_STATE_DISABLING && + priv->rf_enabled) { + /* Called when the ModemManager modem enabled state is changed externally + * to NetworkManager (eg something using MM's D-Bus API directly). + */ + if (nm_device_is_activating (device) || dev_state == NM_DEVICE_STATE_ACTIVATED) { + /* user-initiated action, hence DISCONNECTED not FAILED */ + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + return; + } + } + + if (new_state < NM_MODEM_STATE_CONNECTING && + old_state >= NM_MODEM_STATE_CONNECTING && + dev_state >= NM_DEVICE_STATE_NEED_AUTH && + dev_state <= NM_DEVICE_STATE_ACTIVATED) { + /* Fail the device if the modem disconnects unexpectedly while the + * device is activating/activated. */ + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER); + return; + } + + if (new_state > NM_MODEM_STATE_LOCKED && old_state == NM_MODEM_STATE_LOCKED) { + /* If the modem is now unlocked, enable/disable it according to the + * device's enabled/disabled state. + */ + nm_modem_set_mm_enabled (priv->modem, priv->rf_enabled); + } + + if ((dev_state >= NM_DEVICE_STATE_DISCONNECTED) && !nm_device_is_available (device)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_MODEM_FAILED); + return; + } + + if ((dev_state == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_MODEM_AVAILABLE); + return; + } +} + +static void +modem_removed_cb (NMModem *modem, gpointer user_data) +{ + g_signal_emit_by_name (NM_DEVICE (user_data), NM_DEVICE_REMOVED); +} + +/*****************************************************************************/ + +static gboolean +owns_iface (NMDevice *device, const char *iface) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + g_assert (priv->modem); + return nm_modem_owns_port (priv->modem, iface); +} + +/*****************************************************************************/ + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + NMConnection *connection = nm_device_get_connection (device); + + g_assert (priv->modem); + + if (new_state == NM_DEVICE_STATE_UNAVAILABLE && + old_state < NM_DEVICE_STATE_UNAVAILABLE) { + /* Log initial modem state */ + nm_log_info (LOGD_MB, "(%s): modem state '%s'", + nm_device_get_iface (device), + nm_modem_state_to_string (nm_modem_get_state (priv->modem))); + } + + nm_modem_device_state_changed (priv->modem, new_state, old_state, reason); + + switch (reason) { + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: + case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: + case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: + case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: + case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: + case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: + case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: + /* Block autoconnect of the just-failed connection for situations + * where a retry attempt would just fail again. + */ + if (connection) + nm_settings_connection_set_autoconnect_blocked_reason (NM_SETTINGS_CONNECTION (connection), reason); + break; + default: + break; + } +} + +static guint +get_hw_address_length (NMDevice *device, gboolean *out_permanent) +{ + return 0; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + if (!NM_DEVICE_CLASS (nm_device_modem_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + return nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, connection); +} + +static gboolean +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + NMModemState state; + + if (!priv->rf_enabled || !priv->modem) + return FALSE; + + state = nm_modem_get_state (priv->modem); + if (state <= NM_MODEM_STATE_INITIALIZING) + return FALSE; + + if (state == NM_MODEM_STATE_LOCKED) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (connection); + + /* Can't use a connection without a PIN if the modem is locked */ + if (!s_gsm || !nm_setting_gsm_get_pin (s_gsm)) + return FALSE; + } + + return TRUE; +} + +static gboolean +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + return nm_modem_complete_connection (priv->modem, connection, existing_connections, error); +} + +static void +deactivate (NMDevice *device) +{ + nm_modem_deactivate (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device); +} + +static NMActStageReturn +act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) +{ + NMActStageReturn ret; + NMActRequest *req; + + ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage1_prepare (device, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + + req = nm_device_get_act_request (device); + g_assert (req); + + return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, reason); +} + +static NMActStageReturn +act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMActRequest *req; + + req = nm_device_get_act_request (device); + g_assert (req); + + return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, reason); +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *reason) +{ + return nm_modem_stage3_ip4_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, + device, + NM_DEVICE_CLASS (nm_device_modem_parent_class), + reason); +} + +static void +ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) +{ + nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device, config); +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *reason) +{ + return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, + device, + NM_DEVICE_CLASS (nm_device_modem_parent_class), + reason); +} + +/*****************************************************************************/ + +static gboolean +get_enabled (NMDevice *device) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + NMModemState modem_state = nm_modem_get_state (priv->modem); + + return priv->rf_enabled && (modem_state >= NM_MODEM_STATE_LOCKED); +} + +static void +set_enabled (NMDevice *device, gboolean enabled) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + + /* Called only by the Manager in response to rfkill switch changes or + * global user WWAN enable/disable preference changes. + */ + priv->rf_enabled = enabled; + + if (priv->modem) { + /* Sync the ModemManager modem enabled/disabled with rfkill/user preference */ + nm_modem_set_mm_enabled (priv->modem, enabled); + } + + if (enabled == FALSE) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_NONE); + } +} + +static gboolean +is_available (NMDevice *dev) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (dev); + NMModemState modem_state; + + if (!priv->rf_enabled) { + nm_log_dbg (LOGD_MB, "(%s): not available because WWAN airplane mode is on", + nm_device_get_iface (dev)); + return FALSE; + } + + g_assert (priv->modem); + modem_state = nm_modem_get_state (priv->modem); + if (modem_state <= NM_MODEM_STATE_INITIALIZING) { + nm_log_dbg (LOGD_MB, "(%s): not available because modem is not ready (%s)", + nm_device_get_iface (dev), + nm_modem_state_to_string (modem_state)); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +NMDevice * +nm_device_modem_new (NMModem *modem) +{ + NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE; + NMDeviceModemCapabilities current_caps = NM_DEVICE_MODEM_CAPABILITY_NONE; + NMDevice *device; + const char *data_port; + + g_return_val_if_fail (NM_IS_MODEM (modem), NULL); + + /* Load capabilities */ + nm_modem_get_capabilities (modem, &caps, ¤t_caps); + + device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_MODEM, + NM_DEVICE_UDI, nm_modem_get_path (modem), + NM_DEVICE_IFACE, nm_modem_get_uid (modem), + NM_DEVICE_DRIVER, nm_modem_get_driver (modem), + NM_DEVICE_TYPE_DESC, "Broadband", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM, + NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN, + NM_DEVICE_MODEM_MODEM, modem, + NM_DEVICE_MODEM_CAPABILITIES, caps, + NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps, + NULL); + + /* If the data port is known, set it as the IP interface immediately */ + data_port = nm_modem_get_data_port (modem); + if (data_port) + nm_device_set_ip_iface (device, data_port); + + return device; +} + +static void +nm_device_modem_init (NMDeviceModem *self) +{ +} + +static void +set_modem (NMDeviceModem *self, NMModem *modem) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + + g_return_if_fail (modem != NULL); + + priv->modem = g_object_ref (modem); + + g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self); + g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self); + g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self); + g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self); + g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self); + g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self); + g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self); + + /* In the old ModemManager the data port is known from the very beginning; + * while in the new ModemManager the data port is set afterwards when the bearer gets + * created */ + g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MODEM: + /* construct-only */ + set_modem (NM_DEVICE_MODEM (object), g_value_get_object (value)); + break; + case PROP_CAPABILITIES: + priv->caps = g_value_get_uint (value); + break; + case PROP_CURRENT_CAPABILITIES: + priv->current_caps = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MODEM: + g_value_set_object (value, priv->modem); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, priv->caps); + break; + case PROP_CURRENT_CAPABILITIES: + g_value_set_uint (value, priv->current_caps); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); + + if (priv->modem) + g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object)); + g_clear_object (&priv->modem); + + G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object); +} + +static void +nm_device_modem_class_init (NMDeviceModemClass *mclass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (mclass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass); + + g_type_class_add_private (object_class, sizeof (NMDeviceModemPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + device_class->get_hw_address_length = get_hw_address_length; + device_class->check_connection_compatible = check_connection_compatible; + device_class->check_connection_available = check_connection_available; + device_class->complete_connection = complete_connection; + device_class->deactivate = deactivate; + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->act_stage2_config = act_stage2_config; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->ip4_config_pre_commit = ip4_config_pre_commit; + device_class->get_enabled = get_enabled; + device_class->set_enabled = set_enabled; + device_class->owns_iface = owns_iface; + device_class->is_available = is_available; + + device_class->state_changed = device_state_changed; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_MODEM, + g_param_spec_object (NM_DEVICE_MODEM_MODEM, + "Modem", + "Modem", + NM_TYPE_MODEM, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_MODEM_CAPABILITIES, + "Modem Capabilities", + "Modem Capabilities", + 0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_CURRENT_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_MODEM_CURRENT_CAPABILITIES, + "Current modem Capabilities", + "Current modem Capabilities", + 0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (mclass), + &dbus_glib_nm_device_modem_object_info); +} diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h new file mode 100644 index 00000000..ee6b2988 --- /dev/null +++ b/src/devices/wwan/nm-device-modem.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2011 Red Hat, Inc. + */ + +#ifndef NM_DEVICE_MODEM_H +#define NM_DEVICE_MODEM_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-device.h" +#include "nm-modem.h" + +#define NM_TYPE_DEVICE_MODEM (nm_device_modem_get_type ()) +#define NM_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem)) +#define NM_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) +#define NM_IS_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_MODEM)) +#define NM_IS_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_MODEM)) +#define NM_DEVICE_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass)) + +#define NM_DEVICE_MODEM_MODEM "modem" +#define NM_DEVICE_MODEM_CAPABILITIES "modem-capabilities" +#define NM_DEVICE_MODEM_CURRENT_CAPABILITIES "current-capabilities" + +typedef struct { + NMDevice parent; +} NMDeviceModem; + +typedef struct { + NMDeviceClass parent; + +} NMDeviceModemClass; + +GType nm_device_modem_get_type (void); + +NMDevice *nm_device_modem_new (NMModem *modem); + +#endif /* NM_DEVICE_MODEM_H */ diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c new file mode 100644 index 00000000..807b8924 --- /dev/null +++ b/src/devices/wwan/nm-modem-broadband.c @@ -0,0 +1,987 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org> + */ + +#include <glib/gi18n.h> +#include <string.h> +#include <libmm-glib.h> +#include "nm-modem-broadband.h" +#include "nm-setting-connection.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-device-private.h" + +G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM) + +struct _NMModemBroadbandPrivate { + /* The modem object from dbus */ + MMObject *modem_object; + /* Per-interface objects */ + MMModem *modem_iface; + MMModemSimple *simple_iface; + + /* Connection setup */ + MMSimpleConnectProperties *connect_properties; + MMBearer *bearer; + MMBearerIpConfig *ipv4_config; + MMBearerIpConfig *ipv6_config; + + guint32 pin_tries; +}; + +enum { + PROP_0, + PROP_MODEM, +}; + +#define MODEM_CAPS_3GPP(caps) (caps & (MM_MODEM_CAPABILITY_GSM_UMTS | \ + MM_MODEM_CAPABILITY_LTE | \ + MM_MODEM_CAPABILITY_LTE_ADVANCED)) + +#define MODEM_CAPS_3GPP2(caps) (caps & (MM_MODEM_CAPABILITY_CDMA_EVDO)) + +/* Maximum time to keep the DBus call waiting for a connection result */ +#define MODEM_CONNECT_TIMEOUT_SECS 120 + +/*****************************************************************************/ + +static NMDeviceStateReason +translate_mm_error (GError *error) +{ + NMDeviceStateReason reason; + + g_return_val_if_fail (error != NULL, NM_DEVICE_STATE_REASON_UNKNOWN); + + if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER)) + reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER; + else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_DIALTONE)) + reason = NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE; + else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_BUSY)) + reason = NM_DEVICE_STATE_REASON_MODEM_BUSY; + else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_ANSWER)) + reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_WRONG; + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD)) + reason = NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT; + else { + /* unable to map the ModemManager error to a NM_DEVICE_STATE_REASON */ + nm_log_dbg (LOGD_MB, "unmapped error detected: '%s'", error->message); + reason = NM_DEVICE_STATE_REASON_UNKNOWN; + } + + return reason; +} + +/*****************************************************************************/ + +static void +get_capabilities (NMModem *_self, + NMDeviceModemCapabilities *modem_caps, + NMDeviceModemCapabilities *current_caps) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + MMModemCapability all_supported = MM_MODEM_CAPABILITY_NONE; + MMModemCapability *supported; + guint n_supported; + + /* For now, we don't care about the capability combinations, just merge all + * combinations in a single mask */ + if (mm_modem_get_supported_capabilities (self->priv->modem_iface, &supported, &n_supported)) { + guint i; + + for (i = 0; i < n_supported; i++) + all_supported |= supported[i]; + + g_free (supported); + } + + *modem_caps = (NMDeviceModemCapabilities) all_supported; + *current_caps = (NMDeviceModemCapabilities) mm_modem_get_current_capabilities (self->priv->modem_iface); +} + +static gboolean +owns_port (NMModem *_self, const char *iface) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + const MMModemPortInfo *ports = NULL; + guint n_ports = 0, i; + gboolean owns = FALSE; + + mm_modem_peek_ports (self->priv->modem_iface, &ports, &n_ports); + for (i = 0; i < n_ports && !owns; i++) + owns = (g_strcmp0 (iface, ports[i].name) == 0); + return owns; +} + +/*****************************************************************************/ + +static void +ask_for_pin (NMModemBroadband *self) +{ + guint32 tries; + + tries = self->priv->pin_tries++; + nm_modem_get_secrets (NM_MODEM (self), + NM_SETTING_GSM_SETTING_NAME, + tries ? TRUE : FALSE, + NM_SETTING_GSM_PIN); +} + +static void +connect_ready (MMModemSimple *simple_iface, + GAsyncResult *res, + NMModemBroadband *self) +{ + GError *error = NULL; + guint ip_method; + + g_clear_object (&self->priv->connect_properties); + + self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); + if (!self->priv->bearer) { + if (g_error_matches (error, + MM_MOBILE_EQUIPMENT_ERROR, + MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || + (g_error_matches (error, + MM_CORE_ERROR, + MM_CORE_ERROR_UNAUTHORIZED) && + mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) { + /* Request PIN */ + ask_for_pin (self); + } else { + /* Strip remote error info before logging it */ + if (g_dbus_error_is_remote_error (error)) + g_dbus_error_strip_remote_error (error); + + nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s", + nm_modem_get_uid (NM_MODEM (self)), + error && error->message ? error->message : "(unknown)"); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); + } + + g_clear_error (&error); + g_object_unref (self); + return; + } + + /* Grab IP configurations */ + self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer); + self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer); + + switch (mm_bearer_ip_config_get_method (self->priv->ipv4_config)) { + case MM_BEARER_IP_METHOD_PPP: + ip_method = MM_MODEM_IP_METHOD_PPP; + break; + case MM_BEARER_IP_METHOD_STATIC: + ip_method = MM_MODEM_IP_METHOD_STATIC; + break; + case MM_BEARER_IP_METHOD_DHCP: + ip_method = MM_MODEM_IP_METHOD_DHCP; + break; + default: + error = g_error_new (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "invalid IP config"); + nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s", + nm_modem_get_uid (NM_MODEM (self)), + error->message); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); + g_error_free (error); + g_object_unref (self); + return; + } + + /* IPv4 for now only */ + g_object_set (self, + NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->priv->bearer), + NM_MODEM_IP_METHOD, ip_method, + NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer), + NULL); + + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE); + g_object_unref (self); +} + +static MMSimpleConnectProperties * +create_cdma_connect_properties (NMConnection *connection) +{ + NMSettingCdma *setting; + MMSimpleConnectProperties *properties; + const gchar *str; + + setting = nm_connection_get_setting_cdma (connection); + properties = mm_simple_connect_properties_new (); + + str = nm_setting_cdma_get_number (setting); + if (str) + mm_simple_connect_properties_set_number (properties, str); + + return properties; +} + +static MMSimpleConnectProperties * +create_gsm_connect_properties (NMConnection *connection) +{ + NMSettingGsm *setting; + NMSettingPPP *s_ppp; + MMSimpleConnectProperties *properties; + const gchar *str; + + setting = nm_connection_get_setting_gsm (connection); + properties = mm_simple_connect_properties_new (); + + /* TODO: not needed */ + str = nm_setting_gsm_get_number (setting); + if (str) + mm_simple_connect_properties_set_number (properties, str); + + str = nm_setting_gsm_get_apn (setting); + if (str) + mm_simple_connect_properties_set_apn (properties, str); + + str = nm_setting_gsm_get_network_id (setting); + if (str) + mm_simple_connect_properties_set_operator_id (properties, str); + + str = nm_setting_gsm_get_pin (setting); + if (str) + mm_simple_connect_properties_set_pin (properties, str); + + str = nm_setting_gsm_get_username (setting); + if (str) + mm_simple_connect_properties_set_user (properties, str); + + str = nm_setting_gsm_get_password (setting); + if (str) + mm_simple_connect_properties_set_password (properties, str); + + /* Roaming */ + if (nm_setting_gsm_get_home_only (setting)) + mm_simple_connect_properties_set_allow_roaming (properties, FALSE); + + /* For IpMethod == STATIC or DHCP */ + s_ppp = nm_connection_get_setting_ppp (connection); + if (s_ppp) { + MMBearerAllowedAuth allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; + + if (nm_setting_ppp_get_noauth (s_ppp)) + allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE; + if (!nm_setting_ppp_get_refuse_pap (s_ppp)) + allowed_auth |= MM_BEARER_ALLOWED_AUTH_PAP; + if (!nm_setting_ppp_get_refuse_chap (s_ppp)) + allowed_auth |= MM_BEARER_ALLOWED_AUTH_CHAP; + if (!nm_setting_ppp_get_refuse_mschap (s_ppp)) + allowed_auth |= MM_BEARER_ALLOWED_AUTH_MSCHAP; + if (!nm_setting_ppp_get_refuse_mschapv2 (s_ppp)) + allowed_auth |= MM_BEARER_ALLOWED_AUTH_MSCHAPV2; + if (!nm_setting_ppp_get_refuse_eap (s_ppp)) + allowed_auth |= MM_BEARER_ALLOWED_AUTH_EAP; + + mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth); + } + + return properties; +} + +static NMActStageReturn +act_stage1_prepare (NMModem *_self, + NMConnection *connection, + NMDeviceStateReason *reason) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + MMModemCapability caps; + + g_clear_object (&self->priv->connect_properties); + + caps = mm_modem_get_current_capabilities (self->priv->modem_iface); + if (MODEM_CAPS_3GPP (caps)) + self->priv->connect_properties = create_gsm_connect_properties (connection); + else if (MODEM_CAPS_3GPP2 (caps)) + self->priv->connect_properties = create_cdma_connect_properties (connection); + else { + nm_log_warn (LOGD_MB, "(%s) not a mobile broadband modem", + nm_modem_get_uid (NM_MODEM (self))); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + if (!self->priv->simple_iface) + self->priv->simple_iface = mm_object_get_modem_simple (self->priv->modem_object); + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->priv->simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000); + mm_modem_simple_connect (self->priv->simple_iface, + self->priv->connect_properties, + NULL, + (GAsyncReadyCallback)connect_ready, + g_object_ref (self)); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ + +static gboolean +check_connection_compatible (NMModem *_self, NMConnection *connection) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + MMModemCapability modem_caps; + NMSettingConnection *s_con; + + modem_caps = mm_modem_get_current_capabilities (self->priv->modem_iface); + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + if (MODEM_CAPS_3GPP (modem_caps)) { + NMSettingGsm *s_gsm; + + if (!g_str_equal (nm_setting_connection_get_connection_type (s_con), + NM_SETTING_GSM_SETTING_NAME)) + return FALSE; + + s_gsm = nm_connection_get_setting_gsm (connection); + if (!s_gsm) + return FALSE; + + return TRUE; + } + + if (MODEM_CAPS_3GPP2 (modem_caps)) { + NMSettingCdma *s_cdma; + + if (!g_str_equal (nm_setting_connection_get_connection_type (s_con), + NM_SETTING_CDMA_SETTING_NAME)) + return FALSE; + + s_cdma = nm_connection_get_setting_cdma (connection); + if (!s_cdma) + return FALSE; + + return TRUE; + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +complete_connection (NMModem *_self, + NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + MMModemCapability modem_caps; + NMSettingPPP *s_ppp; + + modem_caps = mm_modem_get_current_capabilities (self->priv->modem_iface); + + /* PPP settings common to 3GPP and 3GPP2 */ + s_ppp = nm_connection_get_setting_ppp (connection); + if (!s_ppp) { + s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); + g_object_set (G_OBJECT (s_ppp), + NM_SETTING_PPP_LCP_ECHO_FAILURE, 5, + NM_SETTING_PPP_LCP_ECHO_INTERVAL, 30, + NULL); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); + } + + if (MODEM_CAPS_3GPP (modem_caps)) { + NMSettingGsm *s_gsm; + + s_gsm = nm_connection_get_setting_gsm (connection); + if (!s_gsm || !nm_setting_gsm_get_apn (s_gsm)) { + /* Need an APN at least */ + g_set_error_literal (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_MISSING_PROPERTY, + NM_SETTING_GSM_APN); + return FALSE; + } + + /* TODO: This is not needed */ + if (!nm_setting_gsm_get_number (s_gsm)) + g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); + + nm_utils_complete_generic (connection, + NM_SETTING_GSM_SETTING_NAME, + existing_connections, + _("GSM connection %d"), + NULL, + FALSE); /* No IPv6 yet by default */ + + return TRUE; + } + + if (MODEM_CAPS_3GPP2 (modem_caps)) { + NMSettingCdma *s_cdma; + + s_cdma = nm_connection_get_setting_cdma (connection); + if (!s_cdma) { + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + } + + if (!nm_setting_cdma_get_number (s_cdma)) + g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL); + + nm_utils_complete_generic (connection, + NM_SETTING_CDMA_SETTING_NAME, + existing_connections, + _("CDMA connection %d"), + NULL, + FALSE); /* No IPv6 yet by default */ + + return TRUE; + } + + g_set_error (error, + NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, + "Device is not a mobile broadband modem"); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +get_user_pass (NMModem *modem, + NMConnection *connection, + const char **user, + const char **pass) +{ + NMSettingGsm *s_gsm; + NMSettingCdma *s_cdma; + + s_gsm = nm_connection_get_setting_gsm (connection); + s_cdma = nm_connection_get_setting_cdma (connection); + if (!s_gsm && !s_cdma) + return FALSE; + + if (user) { + if (s_gsm) + *user = nm_setting_gsm_get_username (s_gsm); + else if (s_cdma) + *user = nm_setting_cdma_get_username (s_cdma); + } + if (pass) { + if (s_gsm) + *pass = nm_setting_gsm_get_password (s_gsm); + else if (s_cdma) + *pass = nm_setting_cdma_get_password (s_cdma); + } + + return TRUE; +} + +/*****************************************************************************/ +/* Query/Update enabled state */ + +static void +set_power_state_low_ready (MMModem *modem, + GAsyncResult *result, + NMModemBroadband *self) +{ + GError *error = NULL; + + if (!mm_modem_set_power_state_finish (modem, result, &error)) { + /* Log but ignore errors; not all modems support low power state */ + nm_log_dbg (LOGD_MB, "(%s) failed to set modem low power state: %s", + nm_modem_get_uid (NM_MODEM (self)), + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } + + /* Balance refcount */ + g_object_unref (self); +} + +static void +modem_disable_ready (MMModem *modem_iface, + GAsyncResult *res, + NMModemBroadband *self) +{ + GError *error = NULL; + + if (mm_modem_disable_finish (modem_iface, res, &error)) { + /* Once disabled, move to low-power mode */ + mm_modem_set_power_state (modem_iface, + MM_MODEM_POWER_STATE_LOW, + NULL, + (GAsyncReadyCallback) set_power_state_low_ready, + g_object_ref (self)); + } else { + nm_log_warn (LOGD_MB, "(%s) failed to disable modem: %s", + nm_modem_get_uid (NM_MODEM (self)), + error && error->message ? error->message : "(unknown)"); + nm_modem_set_prev_state (NM_MODEM (self), "disable failed"); + g_clear_error (&error); + } + + /* Balance refcount */ + g_object_unref (self); +} + +static void +modem_enable_ready (MMModem *modem_iface, + GAsyncResult *res, + NMModemBroadband *self) +{ + GError *error = NULL; + + if (!mm_modem_enable_finish (modem_iface, res, &error)) { + nm_log_warn (LOGD_MB, "(%s) failed to enable modem: %s", + nm_modem_get_uid (NM_MODEM (self)), + error && error->message ? error->message : "(unknown)"); + nm_modem_set_prev_state (NM_MODEM (self), "enable failed"); + g_clear_error (&error); + } + + /* Balance refcount */ + g_object_unref (self); +} + +static void +set_mm_enabled (NMModem *_self, + gboolean enabled) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + + if (enabled) { + mm_modem_enable (self->priv->modem_iface, + NULL, /* cancellable */ + (GAsyncReadyCallback)modem_enable_ready, + g_object_ref (self)); + } else { + mm_modem_disable (self->priv->modem_iface, + NULL, /* cancellable */ + (GAsyncReadyCallback)modem_disable_ready, + g_object_ref (self)); + } +} + +/*****************************************************************************/ +/* IP method static */ + +static gboolean +ip_string_to_network_address (const gchar *str, + guint32 *out) +{ + guint32 addr = 0; + gboolean success = FALSE; + + if (!str || inet_pton (AF_INET, str, &addr) != 1) + addr = 0; + else + success = TRUE; + + *out = (guint32)addr; + return success; +} + +static gboolean +static_stage3_done (NMModemBroadband *self) +{ + GError *error = NULL; + NMIP4Config *config = NULL; + const gchar *address_string; + const gchar *gw_string; + guint32 address_network; + guint32 gw; + NMPlatformIP4Address address; + const gchar **dns; + guint i; + + g_assert (self->priv->ipv4_config); + + nm_log_info (LOGD_MB, "(%s): IPv4 static configuration:", + nm_modem_get_uid (NM_MODEM (self))); + + /* Fully fail if invalid IP address retrieved */ + address_string = mm_bearer_ip_config_get_address (self->priv->ipv4_config); + if (!ip_string_to_network_address (address_string, &address_network)) { + error = g_error_new (NM_MODEM_ERROR, + NM_MODEM_ERROR_CONNECTION_INVALID, + "(%s) retrieving IP4 configuration failed: invalid address given '%s'", + nm_modem_get_uid (NM_MODEM (self)), + address_string); + goto out; + } + + /* Missing gateway not a hard failure */ + gw_string = mm_bearer_ip_config_get_gateway (self->priv->ipv4_config); + ip_string_to_network_address (gw_string, &gw); + + config = nm_ip4_config_new (); + + memset (&address, 0, sizeof (address)); + address.address = address_network; + address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config); + address.source = NM_PLATFORM_SOURCE_WWAN; + nm_ip4_config_add_address (config, &address); + + nm_log_info (LOGD_MB, " address %s/%d", address_string, address.plen); + + if (gw) { + nm_ip4_config_set_gateway (config, gw); + nm_log_info (LOGD_MB, " gateway %s", gw_string); + } + + /* DNS servers */ + dns = mm_bearer_ip_config_get_dns (self->priv->ipv4_config); + for (i = 0; dns[i]; i++) { + if ( ip_string_to_network_address (dns[i], &address_network) + && address_network > 0) { + nm_ip4_config_add_nameserver (config, address_network); + nm_log_info (LOGD_MB, " DNS %s", dns[i]); + } + } + +out: + g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, config, error); + g_clear_error (&error); + return FALSE; +} + +static NMActStageReturn +static_stage3_ip4_config_start (NMModem *_self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + + /* We schedule it in an idle just to follow the same logic as in the + * generic modem implementation. */ + g_idle_add ((GSourceFunc)static_stage3_done, self); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ +/* Disconnect */ + +typedef struct { + NMModemBroadband *self; + gboolean warn; +} SimpleDisconnectContext; + +static void +simple_disconnect_context_free (SimpleDisconnectContext *ctx) +{ + g_object_unref (ctx->self); + g_slice_free (SimpleDisconnectContext, ctx); +} + +static void +simple_disconnect_ready (MMModemSimple *modem_iface, + GAsyncResult *res, + SimpleDisconnectContext *ctx) +{ + GError *error = NULL; + + if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) { + if (ctx->warn) + nm_log_warn (LOGD_MB, "(%s) failed to disconnect modem: %s", + nm_modem_get_uid (NM_MODEM (ctx->self)), + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } + + simple_disconnect_context_free (ctx); +} + +static void +disconnect (NMModem *self, + gboolean warn) +{ + SimpleDisconnectContext *ctx; + + ctx = g_slice_new (SimpleDisconnectContext); + ctx->self = g_object_ref (self); + + /* Don't bother warning on FAILED since the modem is already gone */ + ctx->warn = warn; + + mm_modem_simple_disconnect ( + ctx->self->priv->simple_iface, + NULL, /* bearer path; if NULL given ALL get disconnected */ + NULL, /* cancellable */ + (GAsyncReadyCallback)simple_disconnect_ready, + ctx); +} + +/*****************************************************************************/ + +static void +deactivate (NMModem *_self, NMDevice *device) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + + /* TODO: cancel SimpleConnect() if any */ + + /* Cleanup IPv4 addresses and routes */ + g_clear_object (&self->priv->ipv4_config); + g_clear_object (&self->priv->ipv6_config); + g_clear_object (&self->priv->bearer); + + self->priv->pin_tries = 0; + + /* Chain up parent's */ + NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate (_self, device); +} + +/*****************************************************************************/ + +#define MAP_STATE(name) case MM_MODEM_STATE_##name: return NM_MODEM_STATE_##name; + +static NMModemState +mm_state_to_nm (MMModemState mm_state) +{ + switch (mm_state) { + MAP_STATE(UNKNOWN) + MAP_STATE(FAILED) + MAP_STATE(INITIALIZING) + MAP_STATE(LOCKED) + MAP_STATE(DISABLED) + MAP_STATE(DISABLING) + MAP_STATE(ENABLING) + MAP_STATE(ENABLED) + MAP_STATE(SEARCHING) + MAP_STATE(REGISTERED) + MAP_STATE(DISCONNECTING) + MAP_STATE(CONNECTING) + MAP_STATE(CONNECTED) + } + return NM_MODEM_STATE_UNKNOWN; +} + +static void +modem_state_changed (MMModem *modem, + MMModemState old_state, + MMModemState new_state, + MMModemStateChangeReason reason, + NMModemBroadband *self) +{ + + /* After the SIM is unlocked MM1 will move the device to INITIALIZING which + * is an unavailable state. That makes state handling confusing here, so + * suppress this state change and let the modem move from LOCKED to DISABLED. + */ + if (new_state == MM_MODEM_STATE_INITIALIZING && old_state == MM_MODEM_STATE_LOCKED) + return; + + nm_modem_set_state (NM_MODEM (self), + mm_state_to_nm (new_state), + mm_modem_state_change_reason_get_string (reason)); +} + +/*****************************************************************************/ + +NMModem * +nm_modem_broadband_new (GObject *object, GError **error) +{ + NMModem *modem; + MMObject *modem_object; + MMModem *modem_iface; + gchar *drivers; + + g_return_val_if_fail (MM_IS_OBJECT (object), NULL); + modem_object = MM_OBJECT (object); + + /* Ensure we have the 'Modem' interface and the primary port at least */ + modem_iface = mm_object_peek_modem (modem_object); + g_return_val_if_fail (!!modem_iface, NULL); + g_return_val_if_fail (!!mm_modem_get_primary_port (modem_iface), NULL); + + /* Build a single string with all drivers listed */ + drivers = g_strjoinv (", ", (gchar **)mm_modem_get_drivers (modem_iface)); + + modem = g_object_new (NM_TYPE_MODEM_BROADBAND, + NM_MODEM_PATH, mm_object_get_path (modem_object), + NM_MODEM_UID, mm_modem_get_primary_port (modem_iface), + NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface), + NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */ + NM_MODEM_STATE, mm_state_to_nm (mm_modem_get_state (modem_iface)), + NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface), + NM_MODEM_BROADBAND_MODEM, modem_object, + NM_MODEM_DRIVER, drivers, + NULL); + g_free (drivers); + return modem; +} + +static void +get_sim_ready (MMModem *modem, + GAsyncResult *res, + NMModemBroadband *self) +{ + GError *error = NULL; + MMSim *new_sim; + + new_sim = mm_modem_get_sim_finish (modem, res, &error); + if (new_sim) { + g_object_set (G_OBJECT (self), + NM_MODEM_SIM_ID, mm_sim_get_identifier (new_sim), + NULL); + g_object_unref (new_sim); + } else { + nm_log_warn (LOGD_MB, "(%s) failed to retrieve SIM object: %s", + nm_modem_get_uid (NM_MODEM (self)), + error && error->message ? error->message : "(unknown)"); + } + g_clear_error (&error); + g_object_unref (self); +} + +static void +sim_changed (MMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (user_data); + + g_return_if_fail (modem == self->priv->modem_iface); + + if (mm_modem_get_sim_path (self->priv->modem_iface)) { + mm_modem_get_sim (self->priv->modem_iface, + NULL, /* cancellable */ + (GAsyncReadyCallback) get_sim_ready, + g_object_ref (self)); + } else + g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, NULL, NULL); +} + +static void +nm_modem_broadband_init (NMModemBroadband *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + NM_TYPE_MODEM_BROADBAND, + NMModemBroadbandPrivate); +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (object); + + switch (prop_id) { + case PROP_MODEM: + /* construct-only */ + self->priv->modem_object = g_value_dup_object (value); + self->priv->modem_iface = mm_object_get_modem (self->priv->modem_object); + g_assert (self->priv->modem_iface != NULL); + g_signal_connect (self->priv->modem_iface, + "state-changed", + G_CALLBACK (modem_state_changed), + self); + g_signal_connect (self->priv->modem_iface, + "notify::sim", + G_CALLBACK (sim_changed), + self); + sim_changed (self->priv->modem_iface, NULL, self); + + /* Note: don't grab the Simple iface here; the Modem interface is the + * only one assumed to be always valid and available */ + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (object); + + switch (prop_id) { + case PROP_MODEM: + g_value_set_object (value, self->priv->modem_object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (object); + + g_clear_object (&self->priv->ipv4_config); + g_clear_object (&self->priv->ipv6_config); + g_clear_object (&self->priv->bearer); + g_clear_object (&self->priv->modem_iface); + g_clear_object (&self->priv->simple_iface); + g_clear_object (&self->priv->modem_object); + + G_OBJECT_CLASS (nm_modem_broadband_parent_class)->dispose (object); +} + +static void +nm_modem_broadband_class_init (NMModemBroadbandClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemBroadbandPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + modem_class->get_capabilities = get_capabilities; + modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; + modem_class->disconnect = disconnect; + modem_class->deactivate = deactivate; + modem_class->set_mm_enabled = set_mm_enabled; + modem_class->get_user_pass = get_user_pass; + modem_class->check_connection_compatible = check_connection_compatible; + modem_class->complete_connection = complete_connection; + modem_class->act_stage1_prepare = act_stage1_prepare; + modem_class->owns_port = owns_port; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_MODEM, + g_param_spec_object (NM_MODEM_BROADBAND_MODEM, + "Modem", + "Broadband modem object", + MM_GDBUS_TYPE_OBJECT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h new file mode 100644 index 00000000..e12ca68c --- /dev/null +++ b/src/devices/wwan/nm-modem-broadband.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2012 - Aleksander Morgado <aleksander@gnu.org> + */ + +#ifndef NM_MODEM_BROADBAND_H +#define NM_MODEM_BROADBAND_H + +#include <dbus/dbus-glib.h> +#include <glib-object.h> +#include "nm-modem.h" + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_BROADBAND (nm_modem_broadband_get_type ()) +#define NM_MODEM_BROADBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_BROADBAND, NMModemBroadband)) +#define NM_MODEM_BROADBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass)) +#define NM_IS_MODEM_BROADBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_BROADBAND)) +#define NM_IS_MODEM_BROADBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_BROADBAND)) +#define NM_MODEM_BROADBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass)) + +#define NM_MODEM_BROADBAND_MODEM "modem" + +typedef struct _NMModemBroadband NMModemBroadband; +typedef struct _NMModemBroadbandClass NMModemBroadbandClass; +typedef struct _NMModemBroadbandPrivate NMModemBroadbandPrivate; + +struct _NMModemBroadband { + NMModem parent; + NMModemBroadbandPrivate *priv; +}; + +struct _NMModemBroadbandClass { + NMModemClass parent; +}; + +GType nm_modem_broadband_get_type (void); + +NMModem *nm_modem_broadband_new (GObject *object, GError **error); + +G_END_DECLS + +#endif /* NM_MODEM_BROADBAND_H */ diff --git a/src/devices/wwan/nm-modem-enum-types.c b/src/devices/wwan/nm-modem-enum-types.c new file mode 100644 index 00000000..cf59ba2b --- /dev/null +++ b/src/devices/wwan/nm-modem-enum-types.c @@ -0,0 +1,64 @@ + + + +/* Generated by glib-mkenums. Do not edit */ + +#include "nm-modem-enum-types.h" + +#include "nm-modem.h" + +GType +nm_modem_error_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GEnumValue values[] = { + { NM_MODEM_ERROR_CONNECTION_NOT_GSM, "NM_MODEM_ERROR_CONNECTION_NOT_GSM", "ConnectionNotGsm" }, + { NM_MODEM_ERROR_CONNECTION_NOT_CDMA, "NM_MODEM_ERROR_CONNECTION_NOT_CDMA", "ConnectionNotCdma" }, + { NM_MODEM_ERROR_CONNECTION_INVALID, "NM_MODEM_ERROR_CONNECTION_INVALID", "ConnectionInvalid" }, + { NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, "NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" }, + { NM_MODEM_ERROR_INITIALIZATION_FAILED, "NM_MODEM_ERROR_INITIALIZATION_FAILED", "InitializationFailed" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_enum_register_static (g_intern_static_string ("NMModemError"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} +GType +nm_modem_state_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GEnumValue values[] = { + { NM_MODEM_STATE_UNKNOWN, "NM_MODEM_STATE_UNKNOWN", "unknown" }, + { NM_MODEM_STATE_FAILED, "NM_MODEM_STATE_FAILED", "failed" }, + { NM_MODEM_STATE_INITIALIZING, "NM_MODEM_STATE_INITIALIZING", "initializing" }, + { NM_MODEM_STATE_LOCKED, "NM_MODEM_STATE_LOCKED", "locked" }, + { NM_MODEM_STATE_DISABLED, "NM_MODEM_STATE_DISABLED", "disabled" }, + { NM_MODEM_STATE_DISABLING, "NM_MODEM_STATE_DISABLING", "disabling" }, + { NM_MODEM_STATE_ENABLING, "NM_MODEM_STATE_ENABLING", "enabling" }, + { NM_MODEM_STATE_ENABLED, "NM_MODEM_STATE_ENABLED", "enabled" }, + { NM_MODEM_STATE_SEARCHING, "NM_MODEM_STATE_SEARCHING", "searching" }, + { NM_MODEM_STATE_REGISTERED, "NM_MODEM_STATE_REGISTERED", "registered" }, + { NM_MODEM_STATE_DISCONNECTING, "NM_MODEM_STATE_DISCONNECTING", "disconnecting" }, + { NM_MODEM_STATE_CONNECTING, "NM_MODEM_STATE_CONNECTING", "connecting" }, + { NM_MODEM_STATE_CONNECTED, "NM_MODEM_STATE_CONNECTED", "connected" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_enum_register_static (g_intern_static_string ("NMModemState"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + + + diff --git a/src/devices/wwan/nm-modem-enum-types.h b/src/devices/wwan/nm-modem-enum-types.h new file mode 100644 index 00000000..81ccd8d2 --- /dev/null +++ b/src/devices/wwan/nm-modem-enum-types.h @@ -0,0 +1,21 @@ + + + +/* Generated by glib-mkenums. Do not edit */ + +#ifndef __NM_MODEM_ENUM_TYPES_H__ +#define __NM_MODEM_ENUM_TYPES_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS +GType nm_modem_error_get_type (void) G_GNUC_CONST; +#define NM_TYPE_MODEM_ERROR (nm_modem_error_get_type ()) +GType nm_modem_state_get_type (void) G_GNUC_CONST; +#define NM_TYPE_MODEM_STATE (nm_modem_state_get_type ()) +G_END_DECLS + +#endif /* __NM_MODEM_ENUM_TYPES_H__ */ + + + diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c new file mode 100644 index 00000000..c4811700 --- /dev/null +++ b/src/devices/wwan/nm-modem-manager.c @@ -0,0 +1,757 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2014 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + * Copyright (C) 2009 Canonical Ltd. + */ + +#include <string.h> +#include "config.h" +#include "nm-modem-manager.h" +#include "nm-logging.h" +#include "nm-modem.h" +#include "nm-modem-old.h" +#include "nm-dbus-manager.h" +#include "nm-modem-old-types.h" +#include "nm-dbus-glib-types.h" + +#if WITH_MODEM_MANAGER_1 +#include <libmm-glib.h> +#include "nm-modem-broadband.h" +#endif + +#define MODEM_POKE_INTERVAL 120 + +G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT) + +struct _NMModemManagerPrivate { + /* ModemManager < 0.7 */ + NMDBusManager *dbus_mgr; + DBusGProxy *proxy; + guint poke_id; + +#if WITH_MODEM_MANAGER_1 + /* ModemManager >= 0.7 */ + GDBusConnection *dbus_connection; + MMManager *modem_manager_1; + guint modem_manager_1_launch_id; + gboolean old_modem_manager_found; + gboolean new_modem_manager_found; + guint modem_manager_1_name_owner_changed_id; + guint modem_manager_1_object_added_id; + guint modem_manager_1_object_removed_id; +#endif + + /* Common */ + GHashTable *modems; +}; + +enum { + MODEM_ADDED, + LAST_SIGNAL, +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +/************************************************************************/ + +static void +handle_new_modem (NMModemManager *self, NMModem *modem) +{ + const char *path; + + path = nm_modem_get_path (modem); + if (g_hash_table_lookup (self->priv->modems, path)) { + g_warn_if_reached (); + return; + } + + /* Track the new modem */ + g_hash_table_insert (self->priv->modems, g_strdup (path), modem); + g_signal_emit (self, signals[MODEM_ADDED], 0, modem); +} + +/************************************************************************/ +/* Support for ModemManager < 0.7 */ + +static void +clear_modem_manager_support (NMModemManager *self) +{ + if (self->priv->poke_id) { + g_source_remove (self->priv->poke_id); + self->priv->poke_id = 0; + } + + if (self->priv->proxy) { + g_object_unref (self->priv->proxy); + self->priv->proxy = NULL; + } +} + +static void +create_modem (NMModemManager *self, const char *path) +{ + DBusGProxy *proxy; + GError *error = NULL; + NMModem *modem = NULL; + GHashTable *properties; + + if (g_hash_table_lookup (self->priv->modems, path)) { + nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path); + return; + } + + proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (self->priv->dbus_mgr), + MM_OLD_DBUS_SERVICE, + path, + DBUS_INTERFACE_PROPERTIES); + g_assert (proxy); + if (dbus_g_proxy_call_with_timeout (proxy, "GetAll", 15000, &error, + G_TYPE_STRING, MM_OLD_DBUS_INTERFACE_MODEM, + G_TYPE_INVALID, + DBUS_TYPE_G_MAP_OF_VARIANT, &properties, + G_TYPE_INVALID)) { + /* Success, create the modem */ + modem = nm_modem_old_new (path, properties, &error); + if (modem) + handle_new_modem (self, modem); + else { + nm_log_warn (LOGD_MB, "failed to create modem: %s", + error ? error->message : "(unknown)"); + } + g_hash_table_destroy (properties); + } else { + nm_log_warn (LOGD_MB, "could not get modem properties: %s %s", + error ? dbus_g_error_get_name (error) : "(none)", + error ? error->message : "(unknown)"); + } + + g_object_unref (proxy); + g_clear_error (&error); +} + +static void +modem_added (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + create_modem (NM_MODEM_MANAGER (user_data), path); +} + +static void +modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + NMModemManager *self = NM_MODEM_MANAGER (user_data); + NMModem *modem; + + modem = (NMModem *) g_hash_table_lookup (self->priv->modems, path); + if (modem) { + nm_modem_emit_removed (modem); + g_hash_table_remove (self->priv->modems, path); + } +} + +static void +mm_poke_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + GPtrArray *modems; + int i; + + if (dbus_g_proxy_end_call (proxy, call, NULL, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &modems, + G_TYPE_INVALID)) { + /* Don't care about the returned value, just free it */ + for (i = 0; i < modems->len; i++) + g_free ((char *) g_ptr_array_index (modems, i)); + g_ptr_array_free (modems, TRUE); + } + g_object_unref (proxy); +} + +static gboolean +poke_modem_cb (gpointer user_data) +{ + NMModemManager *self = NM_MODEM_MANAGER (user_data); + DBusGConnection *g_connection; + DBusGProxy *proxy; + + g_connection = nm_dbus_manager_get_connection (self->priv->dbus_mgr); + proxy = dbus_g_proxy_new_for_name (g_connection, + MM_OLD_DBUS_SERVICE, + MM_OLD_DBUS_PATH, + MM_OLD_DBUS_INTERFACE); + + nm_log_dbg (LOGD_MB, "Requesting to (re)launch modem-manager..."); + + dbus_g_proxy_begin_call_with_timeout (proxy, + "EnumerateDevices", + mm_poke_cb, + NULL, + NULL, + 5000, + G_TYPE_INVALID); + return TRUE; +} + +static void +enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data) +{ + NMModemManager *manager = NM_MODEM_MANAGER (data); + GPtrArray *modems; + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_MB, "could not get modem list: %s", error->message); + g_error_free (error); + } else { + int i; + + for (i = 0; i < modems->len; i++) { + char *path = (char *) g_ptr_array_index (modems, i); + + create_modem (manager, path); + g_free (path); + } + + g_ptr_array_free (modems, TRUE); + } +} + +#if WITH_MODEM_MANAGER_1 +static void clear_modem_manager_1_support (NMModemManager *self); +#endif + +static void +modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices) +{ + if (self->priv->poke_id) { + g_source_remove (self->priv->poke_id); + self->priv->poke_id = 0; + } + + nm_log_info (LOGD_MB, "modem-manager is now available"); + +#if WITH_MODEM_MANAGER_1 + self->priv->old_modem_manager_found = TRUE; + if (self->priv->new_modem_manager_found) + nm_log_warn (LOGD_MB, "Both the old and the new ModemManager were found"); + else + clear_modem_manager_1_support (self); +#endif + + self->priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (self->priv->dbus_mgr), + MM_OLD_DBUS_SERVICE, MM_OLD_DBUS_PATH, MM_OLD_DBUS_INTERFACE); + + dbus_g_proxy_add_signal (self->priv->proxy, "DeviceAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (self->priv->proxy, "DeviceAdded", + G_CALLBACK (modem_added), self, + NULL); + + dbus_g_proxy_add_signal (self->priv->proxy, "DeviceRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (self->priv->proxy, "DeviceRemoved", + G_CALLBACK (modem_removed), self, + NULL); + + if (enumerate_devices) + dbus_g_proxy_begin_call (self->priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID); +} + +static gboolean +remove_one_modem (gpointer key, gpointer value, gpointer user_data) +{ + nm_modem_emit_removed (NM_MODEM (value)); + return TRUE; +} + +static void +modem_manager_disappeared (NMModemManager *self) +{ + g_hash_table_foreach_remove (self->priv->modems, remove_one_modem, self); + + if (self->priv->proxy) { + g_object_unref (self->priv->proxy); + self->priv->proxy = NULL; + } + + /* Try to activate the modem-manager */ + nm_log_dbg (LOGD_MB, "trying to start the modem manager..."); + poke_modem_cb (self); + self->priv->poke_id = g_timeout_add_seconds (MODEM_POKE_INTERVAL, poke_modem_cb, self); +} + +static void +nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + gboolean old_owner_good; + gboolean new_owner_good; + + /* Can't handle the signal if its not from the modem service */ + if (strcmp (MM_OLD_DBUS_SERVICE, name) != 0) + return; + + old_owner_good = (old_owner && strlen (old_owner)); + new_owner_good = (new_owner && strlen (new_owner)); + + if (!old_owner_good && new_owner_good) { + modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE); + } else if (old_owner_good && !new_owner_good) { + nm_log_info (LOGD_MB, "the modem manager disappeared"); + modem_manager_disappeared (NM_MODEM_MANAGER (user_data)); + } +} + +/************************************************************************/ +/* Support for ModemManager >= 0.7 */ + +#if WITH_MODEM_MANAGER_1 + +static void +modem_manager_1_clear_signals (NMModemManager *self) +{ + if (!self->priv->modem_manager_1) + return; + + if (self->priv->modem_manager_1_name_owner_changed_id) { + if (g_signal_handler_is_connected (self->priv->modem_manager_1, + self->priv->modem_manager_1_name_owner_changed_id)) + g_signal_handler_disconnect (self->priv->modem_manager_1, + self->priv->modem_manager_1_name_owner_changed_id); + self->priv->modem_manager_1_name_owner_changed_id = 0; + } + + if (self->priv->modem_manager_1_object_added_id) { + if (g_signal_handler_is_connected (self->priv->modem_manager_1, + self->priv->modem_manager_1_object_added_id)) + g_signal_handler_disconnect (self->priv->modem_manager_1, + self->priv->modem_manager_1_object_added_id); + self->priv->modem_manager_1_object_added_id = 0; + } + + if (self->priv->modem_manager_1_object_removed_id) { + if (g_signal_handler_is_connected (self->priv->modem_manager_1, + self->priv->modem_manager_1_object_removed_id)) + g_signal_handler_disconnect (self->priv->modem_manager_1, + self->priv->modem_manager_1_object_removed_id); + self->priv->modem_manager_1_object_removed_id = 0; + } +} + +static void +clear_modem_manager_1_support (NMModemManager *self) +{ + if (self->priv->modem_manager_1_launch_id) { + g_source_remove (self->priv->modem_manager_1_launch_id); + self->priv->modem_manager_1_launch_id = 0; + } + + modem_manager_1_clear_signals (self); + g_clear_object (&self->priv->modem_manager_1); + g_clear_object (&self->priv->dbus_connection); +} + +static void +modem_object_added (MMManager *modem_manager, + MMObject *modem_object, + NMModemManager *self) +{ + const gchar *path; + MMModem *modem_iface; + NMModem *modem; + GError *error = NULL; + + /* Ensure we don't have the same modem already */ + path = mm_object_get_path (modem_object); + if (g_hash_table_lookup (self->priv->modems, path)) { + nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path); + return; + } + + /* Ensure we have the 'Modem' interface at least */ + modem_iface = mm_object_peek_modem (modem_object); + if (!modem_iface) { + nm_log_warn (LOGD_MB, "modem with path %s doesn't have the Modem interface, ignoring", path); + return; + } + + /* Ensure we have a primary port reported */ + if (!mm_modem_get_primary_port (modem_iface)) { + nm_log_warn (LOGD_MB, "modem with path %s has unknown primary port, ignoring", path); + return; + } + + /* Create a new modem object */ + modem = nm_modem_broadband_new (G_OBJECT (modem_object), &error); + if (modem) + handle_new_modem (self, modem); + else { + nm_log_warn (LOGD_MB, "failed to create modem: %s", + error ? error->message : "(unknown)"); + } + g_clear_error (&error); +} + +static void +modem_object_removed (MMManager *manager, + MMObject *modem_object, + NMModemManager *self) +{ + NMModem *modem; + const gchar *path; + + path = mm_object_get_path (modem_object); + modem = (NMModem *) g_hash_table_lookup (self->priv->modems, path); + if (!modem) + return; + + nm_modem_emit_removed (modem); + g_hash_table_remove (self->priv->modems, path); +} + +static void +modem_manager_1_available (NMModemManager *self) +{ + GList *modems, *l; + + nm_log_info (LOGD_MB, "ModemManager available in the bus"); + + self->priv->new_modem_manager_found = TRUE; + if (self->priv->old_modem_manager_found) + nm_log_warn (LOGD_MB, "Both the old and the new ModemManager were found"); + else + clear_modem_manager_support (self); + + /* Update initial modems list */ + modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->priv->modem_manager_1)); + for (l = modems; l; l = g_list_next (l)) + modem_object_added (self->priv->modem_manager_1, MM_OBJECT (l->data), self); + g_list_free_full (modems, (GDestroyNotify) g_object_unref); +} + +static void schedule_modem_manager_1_relaunch (NMModemManager *self, + guint n_seconds); +static void ensure_client (NMModemManager *self); + +static void +modem_manager_1_name_owner_changed (MMManager *modem_manager_1, + GParamSpec *pspec, + NMModemManager *self) +{ + gchar *name_owner; + + /* Quit poking, if any */ + if (self->priv->modem_manager_1_launch_id) { + g_source_remove (self->priv->modem_manager_1_launch_id); + self->priv->modem_manager_1_launch_id = 0; + } + + name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager_1)); + if (!name_owner) { + nm_log_info (LOGD_MB, "ModemManager disappeared from bus"); + +#if !HAVE_SYSTEMD + /* If not managed by systemd, schedule relaunch */ + schedule_modem_manager_1_relaunch (self, 0); +#endif + + return; + } + + /* Available! */ + g_free (name_owner); + + /* Hack alert: GDBusObjectManagerClient won't signal neither 'object-added' + * nor 'object-removed' if it was created while there was no ModemManager in + * the bus. This hack avoids this issue until we get a GIO with the fix + * included... */ + modem_manager_1_clear_signals (self); + g_clear_object (&self->priv->modem_manager_1); + ensure_client (self); + + /* Whenever GDBusObjectManagerClient is fixed, we can just do the following: + * modem_manager_1_available (self); + */ +} + +#if !HAVE_SYSTEMD + +static void +modem_manager_1_poke_cb (GDBusConnection *connection, + GAsyncResult *res, + NMModemManager *self) +{ + GError *error = NULL; + GVariant *result; + + result = g_dbus_connection_call_finish (connection, res, &error); + if (error) { + /* Ignore common errors when MM is not installed and such */ + if ( !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_EXEC_FAILED) + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FORK_FAILED) + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FAILED) + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMEOUT) + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) { + nm_log_dbg (LOGD_MB, "error poking ModemManager: %s", error->message); + } + g_error_free (error); + + /* Setup timeout to relaunch */ + schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL); + } else + g_variant_unref (result); + + /* Balance refcount */ + g_object_unref (self); +} + +static void +modem_manager_1_poke (NMModemManager *self) +{ + /* If there is no current owner right away, ensure we poke to get one */ + g_dbus_connection_call (self->priv->dbus_connection, + "org.freedesktop.ModemManager1", + "/org/freedesktop/ModemManager1", + "org.freedesktop.DBus.Peer", + "Ping", + NULL, /* inputs */ + NULL, /* outputs */ + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + (GAsyncReadyCallback)modem_manager_1_poke_cb, /* callback */ + g_object_ref (self)); /* user_data */ +} + +#endif /* HAVE_SYSTEMD */ + +static void +modem_manager_1_check_name_owner (NMModemManager *self) +{ + gchar *name_owner; + + name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->priv->modem_manager_1)); + if (name_owner) { + /* Available! */ + modem_manager_1_available (self); + g_free (name_owner); + return; + } + +#if !HAVE_SYSTEMD + /* If the lifecycle is not managed by systemd, poke */ + modem_manager_1_poke (self); +#endif +} + +static void +manager_new_ready (GObject *source, + GAsyncResult *res, + NMModemManager *self) +{ + /* Note we always get an extra reference to self here */ + + GError *error = NULL; + + g_assert (!self->priv->modem_manager_1); + self->priv->modem_manager_1 = mm_manager_new_finish (res, &error); + if (!self->priv->modem_manager_1) { + /* We're not really supposed to get any error here. If we do get one, + * though, just re-schedule the MMManager creation after some time. + * During this period, name-owner changes won't be followed. */ + nm_log_warn (LOGD_MB, "error creating ModemManager client: %s", error->message); + g_error_free (error); + /* Setup timeout to relaunch */ + schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL); + } else if (self->priv->old_modem_manager_found) { + /* If we found the old MM, abort */ + clear_modem_manager_1_support (self); + } else { + /* Setup signals in the GDBusObjectManagerClient */ + self->priv->modem_manager_1_name_owner_changed_id = + g_signal_connect (self->priv->modem_manager_1, + "notify::name-owner", + G_CALLBACK (modem_manager_1_name_owner_changed), + self); + self->priv->modem_manager_1_object_added_id = + g_signal_connect (self->priv->modem_manager_1, + "object-added", + G_CALLBACK (modem_object_added), + self); + self->priv->modem_manager_1_object_removed_id = + g_signal_connect (self->priv->modem_manager_1, + "object-removed", + G_CALLBACK (modem_object_removed), + self); + + modem_manager_1_check_name_owner (self); + } + + /* Balance refcount */ + g_object_unref (self); +} + +static void +ensure_client (NMModemManager *self) +{ + g_assert (self->priv->dbus_connection); + + /* Create the GDBusObjectManagerClient. We do not request to autostart, as + * we don't really want the MMManager creation to fail. We can always poke + * later on if we want to request the autostart */ + if (!self->priv->modem_manager_1) { + mm_manager_new (self->priv->dbus_connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NULL, + (GAsyncReadyCallback)manager_new_ready, + g_object_ref (self)); + return; + } + + /* If already available, recheck name owner! */ + modem_manager_1_check_name_owner (self); +} + +static void +bus_get_ready (GObject *source, + GAsyncResult *res, + NMModemManager *self) +{ + /* Note we always get an extra reference to self here */ + + GError *error = NULL; + + self->priv->dbus_connection = g_bus_get_finish (res, &error); + if (!self->priv->dbus_connection) { + nm_log_warn (LOGD_CORE, "error getting bus connection: %s", error->message); + g_error_free (error); + /* Setup timeout to relaunch */ + schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL); + } else if (self->priv->old_modem_manager_found) { + /* If we found the old MM, abort */ + clear_modem_manager_1_support (self); + } else { + /* Got the bus, ensure client */ + ensure_client (self); + } + + /* Balance refcount */ + g_object_unref (self); +} + +static gboolean +ensure_bus (NMModemManager *self) +{ + /* Clear launch ID */ + self->priv->modem_manager_1_launch_id = 0; + + if (!self->priv->dbus_connection) + g_bus_get (G_BUS_TYPE_SYSTEM, + NULL, + (GAsyncReadyCallback)bus_get_ready, + g_object_ref (self)); + else + /* If bus is already available, ensure client */ + ensure_client (self); + + return FALSE; +} + +static void +schedule_modem_manager_1_relaunch (NMModemManager *self, + guint n_seconds) +{ + /* No need to pass an extra reference to self; timeout/idle will be + * cancelled if the object gets disposed. */ + + if (n_seconds) + self->priv->modem_manager_1_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)ensure_bus, self); + else + self->priv->modem_manager_1_launch_id = g_idle_add ((GSourceFunc)ensure_bus, self); +} + +#endif /* WITH_MODEM_MANAGER_1 */ + +/************************************************************************/ + +static void +nm_modem_manager_init (NMModemManager *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_MODEM_MANAGER, NMModemManagerPrivate); + + self->priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + /* ModemManager < 0.7 */ + self->priv->dbus_mgr = nm_dbus_manager_get (); + g_signal_connect (self->priv->dbus_mgr, NM_DBUS_MANAGER_NAME_OWNER_CHANGED, + G_CALLBACK (nm_modem_manager_name_owner_changed), + self); + if (nm_dbus_manager_name_has_owner (self->priv->dbus_mgr, MM_OLD_DBUS_SERVICE)) + modem_manager_appeared (self, TRUE); + else + modem_manager_disappeared (self); + +#if WITH_MODEM_MANAGER_1 + /* ModemManager >= 0.7 */ + schedule_modem_manager_1_relaunch (self, 0); +#endif +} + +static void +dispose (GObject *object) +{ + NMModemManager *self = NM_MODEM_MANAGER (object); + + /* ModemManager < 0.7 */ + clear_modem_manager_support (self); + +#if WITH_MODEM_MANAGER_1 + /* ModemManager >= 0.7 */ + clear_modem_manager_1_support (self); +#endif + + if (self->priv->modems) { + g_hash_table_foreach_remove (self->priv->modems, remove_one_modem, object); + g_hash_table_destroy (self->priv->modems); + } + + self->priv->dbus_mgr = NULL; + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object); +} + +static void +nm_modem_manager_class_init (NMModemManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemManagerPrivate)); + + object_class->dispose = dispose; + + signals[MODEM_ADDED] = + g_signal_new (NM_MODEM_MANAGER_MODEM_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemManagerClass, modem_added), + NULL, NULL, NULL, + G_TYPE_NONE, 1, NM_TYPE_MODEM); +} diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h new file mode 100644 index 00000000..3082bdb3 --- /dev/null +++ b/src/devices/wwan/nm-modem-manager.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2014 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + * Copyright (C) 2009 Canonical Ltd. + */ + +#ifndef NM_MODEM_MANAGER_H +#define NM_MODEM_MANAGER_H + +#include <glib-object.h> +#include "nm-modem.h" + +#define NM_TYPE_MODEM_MANAGER (nm_modem_manager_get_type ()) +#define NM_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_MANAGER, NMModemManager)) + +#define NM_MODEM_MANAGER_MODEM_ADDED "modem-added" + +typedef struct _NMModemManagerPrivate NMModemManagerPrivate; + +typedef struct { + GObject parent; + NMModemManagerPrivate *priv; +} NMModemManager; + +typedef struct { + GObjectClass parent; + + void (*modem_added) (NMModemManager *self, NMModem *modem); +} NMModemManagerClass; + +GType nm_modem_manager_get_type (void); + +#endif /* NM_MODEM_MANAGER_H */ diff --git a/src/devices/wwan/nm-modem-old-types.h b/src/devices/wwan/nm-modem-old-types.h new file mode 100644 index 00000000..84065a68 --- /dev/null +++ b/src/devices/wwan/nm-modem-old-types.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_OLD_TYPES_H +#define NM_MODEM_OLD_TYPES_H + +#define MM_OLD_DBUS_SERVICE "org.freedesktop.ModemManager" +#define MM_OLD_DBUS_PATH "/org/freedesktop/ModemManager" +#define MM_OLD_DBUS_INTERFACE "org.freedesktop.ModemManager" +#define MM_OLD_DBUS_INTERFACE_MODEM "org.freedesktop.ModemManager.Modem" +#define MM_OLD_DBUS_INTERFACE_MODEM_SIMPLE "org.freedesktop.ModemManager.Modem.Simple" +#define MM_OLD_DBUS_INTERFACE_MODEM_CDMA "org.freedesktop.ModemManager.Modem.Cdma" +#define MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD "org.freedesktop.ModemManager.Modem.Gsm.Card" +#define MM_OLD_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network" + +#define MM_OLD_MODEM_TYPE_UNKNOWN 0 +#define MM_OLD_MODEM_TYPE_GSM 1 +#define MM_OLD_MODEM_TYPE_CDMA 2 + +/* Errors */ + +#define MM_OLD_MODEM_CONNECT_ERROR_NO_CARRIER MM_OLD_DBUS_INTERFACE_MODEM ".NoCarrier" +#define MM_OLD_MODEM_CONNECT_ERROR_NO_DIALTONE MM_OLD_DBUS_INTERFACE_MODEM ".NoDialtone" +#define MM_OLD_MODEM_CONNECT_ERROR_BUSY MM_OLD_DBUS_INTERFACE_MODEM ".Busy" +#define MM_OLD_MODEM_CONNECT_ERROR_NO_ANSWER MM_OLD_DBUS_INTERFACE_MODEM ".NoAnswer" + +#define MM_OLD_MODEM_ERROR "org.freedesktop.ModemManager.Modem.Gsm" + +#define MM_OLD_MODEM_ERROR_NETWORK_NOT_ALLOWED MM_OLD_MODEM_ERROR ".NetworkNotAllowed" +#define MM_OLD_MODEM_ERROR_NETWORK_TIMEOUT MM_OLD_MODEM_ERROR ".NetworkTimeout" +#define MM_OLD_MODEM_ERROR_NO_NETWORK MM_OLD_MODEM_ERROR ".NoNetwork" +#define MM_OLD_MODEM_ERROR_SIM_NOT_INSERTED MM_OLD_MODEM_ERROR ".SimNotInserted" +#define MM_OLD_MODEM_ERROR_SIM_PIN MM_OLD_MODEM_ERROR ".SimPinRequired" +#define MM_OLD_MODEM_ERROR_SIM_PUK MM_OLD_MODEM_ERROR ".SimPukRequired" +#define MM_OLD_MODEM_ERROR_SIM_WRONG MM_OLD_MODEM_ERROR ".SimWrong" +#define MM_OLD_MODEM_ERROR_WRONG_PASSWORD MM_OLD_MODEM_ERROR ".IncorrectPassword" + +typedef enum { + MM_OLD_MODEM_STATE_UNKNOWN = 0, + MM_OLD_MODEM_STATE_DISABLED = 10, + MM_OLD_MODEM_STATE_DISABLING = 20, + MM_OLD_MODEM_STATE_ENABLING = 30, + MM_OLD_MODEM_STATE_ENABLED = 40, + MM_OLD_MODEM_STATE_SEARCHING = 50, + MM_OLD_MODEM_STATE_REGISTERED = 60, + MM_OLD_MODEM_STATE_DISCONNECTING = 70, + MM_OLD_MODEM_STATE_CONNECTING = 80, + MM_OLD_MODEM_STATE_CONNECTED = 90, + + MM_OLD_MODEM_STATE_LAST = MM_OLD_MODEM_STATE_CONNECTED +} MMOldModemState; + +#endif /* NM_MODEM_OLD_TYPES_H */ diff --git a/src/devices/wwan/nm-modem-old.c b/src/devices/wwan/nm-modem-old.c new file mode 100644 index 00000000..ce1a382e --- /dev/null +++ b/src/devices/wwan/nm-modem-old.c @@ -0,0 +1,1139 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2013 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include <string.h> +#include <glib/gi18n.h> + +#include "nm-modem-old.h" +#include "nm-dbus-manager.h" +#include "nm-setting-connection.h" +#include "nm-properties-changed-signal.h" +#include "nm-modem-old-types.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-device-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" + +G_DEFINE_TYPE (NMModemOld, nm_modem_old, NM_TYPE_MODEM) + +#define NM_MODEM_OLD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_OLD, NMModemOldPrivate)) + +typedef struct { + DBusGProxy *proxy; + DBusGProxy *props_proxy; + + MMOldModemState state; + NMDeviceModemCapabilities caps; + char *unlock_required; + + DBusGProxyCall *call; + GHashTable *connect_properties; + + guint32 pin_tries; + guint enable_delay_id; +} NMModemOldPrivate; + +#define CAPS_3GPP (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE) + +/*****************************************************************************/ + +typedef enum { + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA, + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA, + + MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA +} MMModemDeprecatedMode; + +typedef enum { + MM_MODEM_GSM_ALLOWED_MODE_ANY = 0, + MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1, + MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2, + MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY = 3, + MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY = 4, + MM_MODEM_GSM_ALLOWED_MODE_4G_PREFERRED = 5, + MM_MODEM_GSM_ALLOWED_MODE_4G_ONLY = 6, + + MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_4G_ONLY +} MMModemGsmAllowedMode; + +typedef enum { + MM_MODEM_GSM_ALLOWED_AUTH_UNKNOWN = 0x0000, + /* bits 0..4 order match Ericsson device bitmap */ + MM_MODEM_GSM_ALLOWED_AUTH_NONE = 0x0001, + MM_MODEM_GSM_ALLOWED_AUTH_PAP = 0x0002, + MM_MODEM_GSM_ALLOWED_AUTH_CHAP = 0x0004, + MM_MODEM_GSM_ALLOWED_AUTH_MSCHAP = 0x0008, + MM_MODEM_GSM_ALLOWED_AUTH_MSCHAPV2 = 0x0010, + MM_MODEM_GSM_ALLOWED_AUTH_EAP = 0x0020, + + MM_MODEM_GSM_ALLOWED_AUTH_LAST = MM_MODEM_GSM_ALLOWED_AUTH_EAP +} MMModemGsmAllowedAuth; + +static NMDeviceStateReason +translate_mm_error (GError *error) +{ + NMDeviceStateReason reason; + + if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_NO_CARRIER)) + reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_NO_DIALTONE)) + reason = NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_BUSY)) + reason = NM_DEVICE_STATE_REASON_MODEM_BUSY; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_NO_ANSWER)) + reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_NETWORK_NOT_ALLOWED)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_NETWORK_TIMEOUT)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_NO_NETWORK)) + reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_NOT_INSERTED)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PIN)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PUK)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_WRONG)) + reason = NM_DEVICE_STATE_REASON_GSM_SIM_WRONG; + else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_WRONG_PASSWORD)) + reason = NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT; + else { + /* unable to map the ModemManager error to a NM_DEVICE_STATE_REASON */ + nm_log_dbg (LOGD_MB, "unmapped dbus error detected: '%s'", dbus_g_error_get_name (error)); + reason = NM_DEVICE_STATE_REASON_UNKNOWN; + } + + /* FIXME: We have only GSM error messages here, and we have no idea which + activation state failed. Reasons like: + NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED, + NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED, + NM_DEVICE_STATE_REASON_GSM_APN_FAILED, + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED, + NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED + are not used. + */ + return reason; +} + +#define MAP_STATE(name) case MM_OLD_MODEM_STATE_##name: return NM_MODEM_STATE_##name; + +static NMModemState +mm_state_to_nm (MMOldModemState mm_state, const char *unlock_required) +{ + if (unlock_required && *unlock_required) + return NM_MODEM_STATE_LOCKED; + + switch (mm_state) { + MAP_STATE(UNKNOWN) + MAP_STATE(DISABLED) + MAP_STATE(DISABLING) + MAP_STATE(ENABLING) + MAP_STATE(ENABLED) + MAP_STATE(SEARCHING) + MAP_STATE(REGISTERED) + MAP_STATE(DISCONNECTING) + MAP_STATE(CONNECTING) + MAP_STATE(CONNECTED) + } + return NM_MODEM_STATE_UNKNOWN; +}; + +/*****************************************************************************/ + +static DBusGProxy * +nm_modem_old_get_proxy (NMModemOld *self, const char *interface) +{ + + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + const char *current_iface; + + g_return_val_if_fail (NM_IS_MODEM_OLD (self), NULL); + + /* Default to the default interface. */ + if (interface == NULL) + interface = MM_OLD_DBUS_INTERFACE_MODEM; + + if (interface && !strcmp (interface, DBUS_INTERFACE_PROPERTIES)) + return priv->props_proxy; + + current_iface = dbus_g_proxy_get_interface (priv->proxy); + if (!current_iface || strcmp (current_iface, interface)) + dbus_g_proxy_set_interface (priv->proxy, interface); + + return priv->proxy; +} + +/*****************************************************************************/ +/* Query/Update enabled state */ + +static void +set_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { + nm_log_warn (LOGD_MB, "failed to enable/disable modem: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + nm_modem_set_prev_state (NM_MODEM (user_data), "enable/disable failed"); + } + /* Wait for the state change signal to indicate enabled state changed */ +} + +static void +set_mm_enabled (NMModem *self, gboolean enabled) +{ + dbus_g_proxy_begin_call (nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM), + "Enable", set_mm_enabled_done, + g_object_ref (self), g_object_unref, + G_TYPE_BOOLEAN, enabled, + G_TYPE_INVALID); +} + +/*****************************************************************************/ + +static void +ask_for_pin (NMModemOld *self, gboolean always_ask) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + guint32 tries = 0; + + g_return_if_fail (NM_IS_MODEM_OLD (self)); + + if (!always_ask) + tries = priv->pin_tries++; + + nm_modem_get_secrets (NM_MODEM (self), + NM_SETTING_GSM_SETTING_NAME, + (tries || always_ask) ? TRUE : FALSE, + NM_SETTING_GSM_PIN); +} + +static void +stage1_prepare_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMModemOld *self = NM_MODEM_OLD (user_data); + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + GError *error = NULL; + gboolean asked = FALSE; + + priv->call = NULL; + + if (priv->connect_properties) { + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = NULL; + } + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE); + else { + if (priv->caps & CAPS_3GPP) { + if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PIN)) { + ask_for_pin (self, FALSE); + asked = TRUE; + } else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_WRONG)) { + ask_for_pin (self, TRUE); + asked = TRUE; + } + } + + if (!asked) { + nm_log_warn (LOGD_MB, "Mobile connection failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); + } + g_error_free (error); + } +} + +static void +do_connect (NMModemOld *self) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + DBusGProxy *proxy; + + proxy = nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM_SIMPLE); + priv->call = dbus_g_proxy_begin_call_with_timeout (proxy, + "Connect", stage1_prepare_done, + self, NULL, 120000, + DBUS_TYPE_G_MAP_OF_VARIANT, priv->connect_properties, + G_TYPE_INVALID); +} + +static void stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data); + +/* do_enable() is used as a GSourceFunc, hence the gboolean return */ +static gboolean +do_enable (NMModemOld *self) +{ + DBusGProxy *proxy; + + g_return_val_if_fail (NM_IS_MODEM_OLD (self), FALSE); + + NM_MODEM_OLD_GET_PRIVATE (self)->enable_delay_id = 0; + proxy = nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM); + dbus_g_proxy_begin_call_with_timeout (proxy, + "Enable", stage1_enable_done, + self, NULL, 20000, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID); + return FALSE; +} + +static void +stage1_pin_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemOld *self = NM_MODEM_OLD (user_data); + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + NMDeviceStateReason reason; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { + /* Success; try to enable the modem again. Wait a few seconds to ensure + * that ModemManager is ready for the enable right after the unlock. + */ + if (priv->enable_delay_id == 0) + priv->enable_delay_id = g_timeout_add_seconds (4, (GSourceFunc) do_enable, self); + } else { + nm_log_warn (LOGD_MB, "GSM PIN unlock failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + /* try to translate the error reason */ + reason = translate_mm_error (error); + if (reason == NM_DEVICE_STATE_REASON_UNKNOWN) + reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; + + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, reason); + g_error_free (error); + } +} + +static void +handle_enable_pin_required (NMModemOld *self) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + const char *pin = NULL; + GValue *value; + DBusGProxy *proxy; + + g_assert (priv->caps & CAPS_3GPP); + + /* See if we have a PIN already */ + value = g_hash_table_lookup (priv->connect_properties, "pin"); + if (value && G_VALUE_HOLDS_STRING (value)) + pin = g_value_get_string (value); + + /* If we do, send it */ + if (pin) { + proxy = nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD); + dbus_g_proxy_begin_call_with_timeout (proxy, + "SendPin", stage1_pin_done, + self, NULL, 10000, + G_TYPE_STRING, pin, + G_TYPE_INVALID); + } else + ask_for_pin (self, FALSE); +} + +static void +stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemOld *self = NM_MODEM_OLD (user_data); + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + NMDeviceStateReason reason; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) + do_connect (self); + else { + if ((priv->caps & CAPS_3GPP) && dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PIN)) + handle_enable_pin_required (self); + else { + nm_log_warn (LOGD_MB, "Modem enable failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + + /* try to translate the error reason */ + reason = translate_mm_error (error); + if (reason == NM_DEVICE_STATE_REASON_UNKNOWN) + reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, reason); + } + + g_error_free (error); + } +} + +static GHashTable * +create_connect_properties (NMConnection *connection) +{ + NMSettingCdma *s_cdma; + NMSettingGsm *s_gsm; + NMSettingPPP *s_ppp; + GHashTable *properties; + const char *str; + + properties = value_hash_create (); + + s_cdma = nm_connection_get_setting_cdma (connection); + if (s_cdma) { + str = nm_setting_cdma_get_number (s_cdma); + if (str) + value_hash_add_str (properties, "number", str); + return properties; + } + + s_gsm = nm_connection_get_setting_gsm (connection); + if (s_gsm) { + str = nm_setting_gsm_get_number (s_gsm); + if (str) + value_hash_add_str (properties, "number", str); + + str = nm_setting_gsm_get_apn (s_gsm); + if (str) + value_hash_add_str (properties, "apn", str); + + str = nm_setting_gsm_get_network_id (s_gsm); + if (str) + value_hash_add_str (properties, "network_id", str); + + str = nm_setting_gsm_get_pin (s_gsm); + if (str) + value_hash_add_str (properties, "pin", str); + + str = nm_setting_gsm_get_username (s_gsm); + if (str) + value_hash_add_str (properties, "username", str); + + str = nm_setting_gsm_get_password (s_gsm); + if (str) + value_hash_add_str (properties, "password", str); + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + /* Add both old and new preferred modes */ + switch (nm_setting_gsm_get_network_type (s_gsm)) { + case NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY); + break; + case NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY); + break; + case NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED); + break; + case NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED); + break; + case NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G: + /* deprecated modes not extended for 4G, so no need to set them here */ + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_4G_PREFERRED); + break; + case NM_SETTING_GSM_NETWORK_TYPE_4G: + /* deprecated modes not extended for 4G, so no need to set them here */ + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_4G_ONLY); + break; + default: + value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY); + value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_ANY); + break; + } +G_GNUC_END_IGNORE_DEPRECATIONS + + /* Roaming */ + if (nm_setting_gsm_get_home_only (s_gsm)) + value_hash_add_bool (properties, "home_only", TRUE); + + /* For IpMethod == STATIC or DHCP */ + s_ppp = nm_connection_get_setting_ppp (connection); + if (s_ppp) { + guint32 auth = MM_MODEM_GSM_ALLOWED_AUTH_UNKNOWN; + + if (nm_setting_ppp_get_noauth (s_ppp)) + auth |= MM_MODEM_GSM_ALLOWED_AUTH_NONE; + if (!nm_setting_ppp_get_refuse_pap (s_ppp)) + auth |= MM_MODEM_GSM_ALLOWED_AUTH_PAP; + if (!nm_setting_ppp_get_refuse_chap (s_ppp)) + auth |= MM_MODEM_GSM_ALLOWED_AUTH_CHAP; + if (!nm_setting_ppp_get_refuse_mschap (s_ppp)) + auth |= MM_MODEM_GSM_ALLOWED_AUTH_MSCHAP; + if (!nm_setting_ppp_get_refuse_mschapv2 (s_ppp)) + auth |= MM_MODEM_GSM_ALLOWED_AUTH_MSCHAPV2; + if (!nm_setting_ppp_get_refuse_eap (s_ppp)) + auth |= MM_MODEM_GSM_ALLOWED_AUTH_EAP; + + if (auth != MM_MODEM_GSM_ALLOWED_AUTH_UNKNOWN) + value_hash_add_uint (properties, "allowed_auth", auth); + } + + return properties; + } + + g_hash_table_destroy (properties); + return NULL; +} + +static NMActStageReturn +act_stage1_prepare (NMModem *modem, + NMConnection *connection, + NMDeviceStateReason *reason) +{ + NMModemOld *self = NM_MODEM_OLD (modem); + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + + if (priv->connect_properties) + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = create_connect_properties (connection); + + if (nm_modem_get_state (modem) >= NM_MODEM_STATE_ENABLING) + do_connect (self); + else + do_enable (self); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ +/* IP method static */ + +static char addr_to_string_buf[INET6_ADDRSTRLEN + 1]; + +static const char * +ip_address_to_string (guint32 numeric) +{ + guint32 temp_addr; + + memset (&addr_to_string_buf, '\0', sizeof (addr_to_string_buf)); + temp_addr = numeric; + + if (inet_ntop (AF_INET, &temp_addr, addr_to_string_buf, INET_ADDRSTRLEN)) { + return addr_to_string_buf; + } else { + nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X", + ntohl (temp_addr)); + return NULL; + } +} + +static void +static_stage3_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMModemOld *self = NM_MODEM_OLD (user_data); + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + GValueArray *ret_array = NULL; + GError *error = NULL; + NMIP4Config *config = NULL; + + priv->call = NULL; + + /* Returned value array is (uuuu): [IP, DNS1, DNS2, DNS3], all in + * network byte order. + */ + if (dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_VALUE_ARRAY, &ret_array, + G_TYPE_INVALID)) { + NMPlatformIP4Address address; + int i; + + config = nm_ip4_config_new (); + memset (&address, 0, sizeof (address)); + + nm_log_info (LOGD_MB, "(%s): IPv4 static configuration:", + nm_modem_get_uid (NM_MODEM (self))); + + /* IP address */ + address.address = g_value_get_uint (g_value_array_get_nth (ret_array, 0)); + address.plen = 32; + address.source = NM_PLATFORM_SOURCE_WWAN; + nm_ip4_config_add_address (config, &address); + + nm_log_info (LOGD_MB, " address %s/%d", + ip_address_to_string (address.address), + address.plen); + + /* DNS servers */ + for (i = 1; i < ret_array->n_values; i++) { + GValue *value = g_value_array_get_nth (ret_array, i); + guint32 tmp = g_value_get_uint (value); + + if (tmp > 0) { + nm_ip4_config_add_nameserver (config, tmp); + nm_log_info (LOGD_MB, " DNS %s", ip_address_to_string (tmp)); + } + } + g_value_array_free (ret_array); + } + + g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, config, error); + g_clear_error (&error); +} + +static NMActStageReturn +static_stage3_ip4_config_start (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemOldPrivate *priv; + + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + priv = NM_MODEM_OLD_GET_PRIVATE (self); + + priv->call = dbus_g_proxy_begin_call (nm_modem_old_get_proxy (NM_MODEM_OLD (self), + MM_OLD_DBUS_INTERFACE_MODEM), + "GetIP4Config", static_stage3_done, + self, NULL, + G_TYPE_INVALID); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ + +static void +disconnect_done (DBusGProxy *proxy, + DBusGProxyCall *call_id, + gpointer user_data) +{ + GError *error = NULL; + gboolean warn = GPOINTER_TO_UINT (user_data); + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID) && warn) { + nm_log_info (LOGD_MB, "disconnect failed: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + } +} + +static void +disconnect (NMModem *self, + gboolean warn) +{ + dbus_g_proxy_begin_call (nm_modem_old_get_proxy (NM_MODEM_OLD (self), + MM_OLD_DBUS_INTERFACE_MODEM), + "Disconnect", + disconnect_done, + GUINT_TO_POINTER (warn), + NULL, + G_TYPE_INVALID); +} + +/*****************************************************************************/ + +static void +deactivate (NMModem *self, NMDevice *device) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + + priv->pin_tries = 0; + + if (priv->call) { + dbus_g_proxy_cancel_call (priv->proxy, priv->call); + priv->call = NULL; + } + + if (priv->enable_delay_id) { + g_source_remove (priv->enable_delay_id); + priv->enable_delay_id = 0; + } + + /* Chain up parent */ + NM_MODEM_CLASS (nm_modem_old_parent_class)->deactivate (self, device); +} + +/*****************************************************************************/ + +static void +modem_properties_changed (DBusGProxy *proxy, + const char *interface, + GHashTable *props, + gpointer user_data) +{ + NMModemOld *self = NM_MODEM_OLD (user_data); + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self); + GValue *value; + gboolean update_state = FALSE; + + if (strcmp (interface, MM_OLD_DBUS_INTERFACE_MODEM) && + strcmp (interface, MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD)) + return; + + value = g_hash_table_lookup (props, "IpMethod"); + if (value && G_VALUE_HOLDS_UINT (value)) { + g_object_set (self, + NM_MODEM_IP_METHOD, g_value_get_uint (value), + NULL); + } + + value = g_hash_table_lookup (props, "SimIdentifier"); + if (value && G_VALUE_HOLDS_STRING (value)) { + const char *sim_id = g_value_get_string (value); + + g_object_set (self, + NM_MODEM_SIM_ID, (sim_id && *sim_id) ? sim_id : NULL, + NULL); + } + + value = g_hash_table_lookup (props, "UnlockRequired"); + if (value && G_VALUE_HOLDS_STRING (value)) { + g_free (priv->unlock_required); + priv->unlock_required = g_value_dup_string (value); + update_state = TRUE; + } + + value = g_hash_table_lookup (props, "State"); + if (value && G_VALUE_HOLDS_UINT (value)) { + priv->state = g_value_get_uint (value); + update_state = TRUE; + } + + if (update_state) { + nm_modem_set_state (NM_MODEM (self), + mm_state_to_nm (priv->state, priv->unlock_required), + NULL); + } +} + +/*****************************************************************************/ + +static gboolean +check_connection_compatible (NMModem *modem, NMConnection *connection) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (modem); + NMSettingConnection *s_con; + gboolean valid_cdma = FALSE, valid_gsm = FALSE; + const char *ctype; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + ctype = nm_setting_connection_get_connection_type (s_con); + g_assert (ctype); + + /* Check for valid CDMA first */ + if (strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME) == 0) + valid_cdma = !!nm_connection_get_setting_cdma (connection); + + if (strcmp (ctype, NM_SETTING_GSM_SETTING_NAME) == 0) + valid_gsm = !!nm_connection_get_setting_gsm (connection); + + /* Validate CDMA */ + if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) { + if (valid_cdma) + return TRUE; + + /* If the modem is only CDMA and the connection is not CDMA, error */ + if ((priv->caps ^ NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) == 0) + return FALSE; + } + + /* Validate 3GPP */ + if (priv->caps & CAPS_3GPP) + return valid_gsm; + + return FALSE; +} + +/*****************************************************************************/ + +static void +complete_ppp_setting (NMConnection *connection) +{ + NMSettingPPP *s_ppp; + + s_ppp = nm_connection_get_setting_ppp (connection); + if (!s_ppp) { + s_ppp = (NMSettingPPP *) nm_setting_ppp_new (); + g_object_set (G_OBJECT (s_ppp), + NM_SETTING_PPP_LCP_ECHO_FAILURE, 5, + NM_SETTING_PPP_LCP_ECHO_INTERVAL, 30, + NULL); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); + } +} + +static gboolean +complete_connection_3gpp (NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + NMSettingGsm *s_gsm; + + s_gsm = nm_connection_get_setting_gsm (connection); + if (!s_gsm || !nm_setting_gsm_get_apn (s_gsm)) { + /* Need an APN at least */ + g_set_error_literal (error, + NM_SETTING_GSM_ERROR, + NM_SETTING_GSM_ERROR_MISSING_PROPERTY, + NM_SETTING_GSM_APN); + return FALSE; + } + + if (!nm_setting_gsm_get_number (s_gsm)) + g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); + + complete_ppp_setting (connection); + + nm_utils_complete_generic (connection, + NM_SETTING_GSM_SETTING_NAME, + existing_connections, + _("GSM connection %d"), + NULL, + FALSE); /* No IPv6 yet by default */ + return TRUE; +} + +static gboolean +complete_connection_cdma (NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + NMSettingCdma *s_cdma; + + s_cdma = nm_connection_get_setting_cdma (connection); + if (!s_cdma) { + s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); + nm_connection_add_setting (connection, NM_SETTING (s_cdma)); + } + + if (!nm_setting_cdma_get_number (s_cdma)) + g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL); + + complete_ppp_setting (connection); + + nm_utils_complete_generic (connection, + NM_SETTING_CDMA_SETTING_NAME, + existing_connections, + _("CDMA connection %d"), + NULL, + FALSE); /* No IPv6 yet by default */ + return TRUE; +} + +static gboolean +complete_connection (NMModem *modem, + NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (modem); + + /* If the modem has LTE, complete as 3GPP */ + if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_LTE) + return complete_connection_3gpp (connection, existing_connections, error); + + /* Otherwise, prefer CDMA on the theory that if the modem has CDMA/EVDO + * that's most likely what the user will be using. + */ + if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) + return complete_connection_cdma (connection, existing_connections, error); + + if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) + return complete_connection_3gpp (connection, existing_connections, error); + + g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, + "Modem had no WWAN capabilities."); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +get_user_pass (NMModem *modem, + NMConnection *connection, + const char **user, + const char **pass) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (modem); + NMSettingCdma *s_cdma; + NMSettingGsm *s_gsm; + + if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) { + s_cdma = nm_connection_get_setting_cdma (connection); + if (s_cdma) { + if (user) + *user = nm_setting_cdma_get_username (s_cdma); + if (pass) + *pass = nm_setting_cdma_get_password (s_cdma); + return TRUE; + } + } + + /* Fall back to GSM; will be used for CDMA devices on LTE networks too */ + s_gsm = nm_connection_get_setting_gsm (connection); + if (s_gsm) { + if (user) + *user = nm_setting_gsm_get_username (s_gsm); + if (pass) + *pass = nm_setting_gsm_get_password (s_gsm); + return TRUE; + } + + return FALSE; +} + +/*****************************************************************************/ + +static void +get_capabilities (NMModem *_self, + NMDeviceModemCapabilities *modem_caps, + NMDeviceModemCapabilities *current_caps) +{ + NMModemOld *self = NM_MODEM_OLD (_self); + + *current_caps = *modem_caps = NM_MODEM_OLD_GET_PRIVATE (self)->caps; +} + +/*****************************************************************************/ + +NMModem * +nm_modem_old_new (const char *path, GHashTable *properties, GError **error) +{ + NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE; + NMModemOld *self; + GHashTableIter iter; + const char *prop; + GValue *value; + const char *data_device = NULL; + const char *driver = NULL; + const char *master_device = NULL; + const char *unlock_required = NULL; + const char *device_id = NULL; + guint32 modem_type = MM_OLD_MODEM_TYPE_UNKNOWN; + guint32 ip_method = MM_MODEM_IP_METHOD_PPP; + guint32 ip_timeout = 0; + MMOldModemState state = MM_OLD_MODEM_STATE_UNKNOWN; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (properties != NULL, NULL); + + g_hash_table_iter_init (&iter, properties); + while (g_hash_table_iter_next (&iter, (gpointer) &prop, (gpointer) &value)) { + if (g_strcmp0 (prop, "Type") == 0) + modem_type = g_value_get_uint (value); + else if (g_strcmp0 (prop, "MasterDevice") == 0) + master_device = g_value_get_string (value); + else if (g_strcmp0 (prop, "IpMethod") == 0) + ip_method = g_value_get_uint (value); + else if (g_strcmp0 (prop, "Device") == 0) + data_device = g_value_get_string (value); + else if (g_strcmp0 (prop, "Driver") == 0) + driver = g_value_get_string (value); + else if (g_strcmp0 (prop, "IpTimeout") == 0) + ip_timeout = g_value_get_uint (value); + else if (g_strcmp0 (prop, "State") == 0) + state = g_value_get_uint (value); + else if (g_strcmp0 (prop, "UnlockRequired") == 0) + unlock_required = g_value_get_string (value); + else if (g_strcmp0 (prop, "DeviceIdentifier") == 0) + device_id = g_value_get_string (value); + } + + if (modem_type == MM_OLD_MODEM_TYPE_UNKNOWN) { + g_set_error (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED, + "Unhandled modem type %d", modem_type); + return NULL; + } + + if (!master_device || !strlen (master_device)) { + g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED, + "Failed to retrieve modem master device."); + return NULL; + } + + if (!driver || !strlen (driver)) { + g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED, + "Failed to retrieve modem driver."); + return NULL; + } + + if (!data_device || !strlen (data_device)) { + g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED, + "Failed to retrieve modem data device."); + return NULL; + } + + self = (NMModemOld *) g_object_new (NM_TYPE_MODEM_OLD, + NM_MODEM_PATH, path, + NM_MODEM_DRIVER, driver, + NM_MODEM_UID, data_device, + NM_MODEM_CONTROL_PORT, NULL, + NM_MODEM_DATA_PORT, data_device, + NM_MODEM_IP_METHOD, ip_method, + NM_MODEM_IP_TIMEOUT, ip_timeout, + NM_MODEM_DEVICE_ID, device_id, + NM_MODEM_STATE, mm_state_to_nm (state, unlock_required), + NULL); + if (self) { + if (modem_type == MM_OLD_MODEM_TYPE_CDMA) + caps |= NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO; + if (modem_type == MM_OLD_MODEM_TYPE_GSM) + caps |= NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS; + + NM_MODEM_OLD_GET_PRIVATE (self)->caps = caps; + NM_MODEM_OLD_GET_PRIVATE (self)->state = state; + NM_MODEM_OLD_GET_PRIVATE (self)->unlock_required = g_strdup (unlock_required); + } + + return (NMModem *) self; +} + +static void +nm_modem_old_init (NMModemOld *self) +{ +} + +static void +get_sim_id_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemOld *self = NM_MODEM_OLD (user_data); + GValue value = G_VALUE_INIT; + + if (dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_VALUE, &value, G_TYPE_INVALID)) { + if (G_VALUE_HOLDS_STRING (&value)) { + const char *sim_id = g_value_get_string (&value); + + if (sim_id && *sim_id) + g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, sim_id, NULL); + } + g_value_unset (&value); + } +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMModemOldPrivate *priv; + DBusGConnection *bus; + + object = G_OBJECT_CLASS (nm_modem_old_parent_class)->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + priv = NM_MODEM_OLD_GET_PRIVATE (object); + + bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); + priv->proxy = dbus_g_proxy_new_for_name (bus, + MM_OLD_DBUS_SERVICE, + nm_modem_get_path (NM_MODEM (object)), + MM_OLD_DBUS_INTERFACE_MODEM); + + priv->props_proxy = dbus_g_proxy_new_for_name (bus, + MM_OLD_DBUS_SERVICE, + nm_modem_get_path (NM_MODEM (object)), + DBUS_INTERFACE_PROPERTIES); + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, + G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->props_proxy, "MmPropertiesChanged", + G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->props_proxy, "MmPropertiesChanged", + G_CALLBACK (modem_properties_changed), + object, + NULL); + + /* Request the SIM ID */ + dbus_g_proxy_begin_call (priv->props_proxy, + "Get", + get_sim_id_done, + g_object_ref (object), g_object_unref, + G_TYPE_STRING, MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD, + G_TYPE_STRING, "SimIdentifier", + G_TYPE_INVALID); + + return object; +} + +static void +dispose (GObject *object) +{ + NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (object); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->props_proxy) { + g_object_unref (priv->props_proxy); + priv->props_proxy = NULL; + } + + if (priv->connect_properties) { + g_hash_table_destroy (priv->connect_properties); + priv->connect_properties = NULL; + } + + if (priv->enable_delay_id) { + g_source_remove (priv->enable_delay_id); + priv->enable_delay_id = 0; + } + + g_free (priv->unlock_required); + priv->unlock_required = NULL; + + G_OBJECT_CLASS (nm_modem_old_parent_class)->dispose (object); +} + +static void +nm_modem_old_class_init (NMModemOldClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemOldPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->dispose = dispose; + + modem_class->get_capabilities = get_capabilities; + modem_class->get_user_pass = get_user_pass; + modem_class->complete_connection = complete_connection; + modem_class->check_connection_compatible = check_connection_compatible; + modem_class->act_stage1_prepare = act_stage1_prepare; + modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; + modem_class->disconnect = disconnect; + modem_class->deactivate = deactivate; + modem_class->set_mm_enabled = set_mm_enabled; +} diff --git a/src/devices/wwan/nm-modem-old.h b/src/devices/wwan/nm-modem-old.h new file mode 100644 index 00000000..65e3db2d --- /dev/null +++ b/src/devices/wwan/nm-modem-old.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2011 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_OLD_H +#define NM_MODEM_OLD_H + +#include <dbus/dbus-glib.h> +#include <glib-object.h> +#include "nm-modem.h" +#include "nm-modem-old-types.h" + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_OLD (nm_modem_old_get_type ()) +#define NM_MODEM_OLD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_OLD, NMModemOld)) +#define NM_MODEM_OLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_OLD, NMModemOldClass)) +#define NM_IS_MODEM_OLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_OLD)) +#define NM_IS_MODEM_OLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_OLD)) +#define NM_MODEM_OLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_OLD, NMModemOldClass)) + +typedef struct { + NMModem parent; +} NMModemOld; + +typedef struct { + NMModemClass parent; +} NMModemOldClass; + +GType nm_modem_old_get_type (void); + +NMModem *nm_modem_old_new (const char *path, GHashTable *properties, GError **error); + +G_END_DECLS + +#endif /* NM_MODEM_OLD_H */ diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c new file mode 100644 index 00000000..fd3b4a36 --- /dev/null +++ b/src/devices/wwan/nm-modem.c @@ -0,0 +1,1119 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2014 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#include <string.h> +#include "nm-modem.h" +#include "nm-platform.h" +#include "nm-dbus-manager.h" +#include "nm-setting-connection.h" +#include "nm-properties-changed-signal.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-device-private.h" +#include "nm-dbus-glib-types.h" +#include "nm-modem-enum-types.h" + +G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT) + +#define NM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM, NMModemPrivate)) + +enum { + PROP_0, + PROP_CONTROL_PORT, + PROP_DATA_PORT, + PROP_PATH, + PROP_UID, + PROP_DRIVER, + PROP_IP_METHOD, + PROP_IP_TIMEOUT, + PROP_STATE, + PROP_DEVICE_ID, + PROP_SIM_ID, + + LAST_PROP +}; + +typedef struct { + char *uid; + char *path; + char *driver; + char *control_port; + char *data_port; + char *ppp_iface; + guint32 ip_method; + NMModemState state; + NMModemState prev_state; /* revert to this state if enable/disable fails */ + char *device_id; + char *sim_id; + + NMPPPManager *ppp_manager; + + NMActRequest *act_request; + guint32 secrets_tries; + guint32 secrets_id; + + guint32 mm_ip_timeout; + + /* PPP stats */ + guint32 in_bytes; + guint32 out_bytes; +} NMModemPrivate; + +enum { + PPP_STATS, + PPP_FAILED, + PREPARE_RESULT, + IP4_CONFIG_RESULT, + AUTH_REQUESTED, + AUTH_RESULT, + REMOVED, + STATE_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +/*****************************************************************************/ + +GQuark +nm_modem_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-modem-error"); + return quark; +} + +/*****************************************************************************/ +/* State/enabled/connected */ + +static const char *state_table[] = { + [NM_MODEM_STATE_UNKNOWN] = "unknown", + [NM_MODEM_STATE_FAILED] = "failed", + [NM_MODEM_STATE_INITIALIZING] = "initializing", + [NM_MODEM_STATE_LOCKED] = "locked", + [NM_MODEM_STATE_DISABLED] = "disabled", + [NM_MODEM_STATE_DISABLING] = "disabling", + [NM_MODEM_STATE_ENABLING] = "enabling", + [NM_MODEM_STATE_ENABLED] = "enabled", + [NM_MODEM_STATE_SEARCHING] = "searching", + [NM_MODEM_STATE_REGISTERED] = "registered", + [NM_MODEM_STATE_DISCONNECTING] = "disconnecting", + [NM_MODEM_STATE_CONNECTING] = "connecting", + [NM_MODEM_STATE_CONNECTED] = "connected", +}; + +const char * +nm_modem_state_to_string (NMModemState state) +{ + if (state >= 0 && state < G_N_ELEMENTS (state_table)) + return state_table[state]; + return NULL; +} + +NMModemState +nm_modem_get_state (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->state; +} + +void +nm_modem_set_state (NMModem *self, + NMModemState new_state, + const char *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMModemState old_state = priv->state; + + priv->prev_state = NM_MODEM_STATE_UNKNOWN; + + if (new_state != old_state) { + nm_log_info (LOGD_MB, "(%s): modem state changed, '%s' --> '%s' (reason: %s)\n", + nm_modem_get_uid (self), + nm_modem_state_to_string (old_state), + nm_modem_state_to_string (new_state), + reason ? reason : "none"); + + priv->state = new_state; + g_object_notify (G_OBJECT (self), NM_MODEM_STATE); + g_signal_emit (self, signals[STATE_CHANGED], 0, new_state, old_state, reason); + } +} + +void +nm_modem_set_prev_state (NMModem *self, const char *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + /* Reset modem to previous state if the state hasn't already changed */ + if (priv->prev_state != NM_MODEM_STATE_UNKNOWN) + nm_modem_set_state (self, priv->prev_state, reason); +} + +void +nm_modem_set_mm_enabled (NMModem *self, + gboolean enabled) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMModemState prev_state = priv->state; + + if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) { + nm_log_dbg (LOGD_MB, "(%s) cannot enable modem: already enabled", + nm_modem_get_uid (self)); + return; + } + if (!enabled && priv->state <= NM_MODEM_STATE_DISABLING) { + nm_log_dbg (LOGD_MB, "(%s) cannot disable modem: already disabled", + nm_modem_get_uid (self)); + return; + } + + if (priv->state <= NM_MODEM_STATE_INITIALIZING) { + nm_log_dbg (LOGD_MB, "(%s) cannot enable/disable modem: initializing or failed", + nm_modem_get_uid (self)); + return; + } else if (priv->state == NM_MODEM_STATE_LOCKED) { + /* Don't try to enable if the modem is locked since that will fail */ + nm_log_warn (LOGD_MB, "(%s) cannot enable/disable modem: locked", + nm_modem_get_uid (self)); + + /* Try to unlock the modem if it's being enabled */ + if (enabled) + g_signal_emit_by_name (self, NM_MODEM_AUTH_REQUESTED, 0); + return; + } + + NM_MODEM_GET_CLASS (self)->set_mm_enabled (self, enabled); + + /* Pre-empt the state change signal */ + nm_modem_set_state (self, + enabled ? NM_MODEM_STATE_ENABLING : NM_MODEM_STATE_DISABLING, + "user preference"); + priv->prev_state = prev_state; +} + +void +nm_modem_emit_removed (NMModem *self) +{ + g_signal_emit (self, signals[REMOVED], 0); +} + +/*****************************************************************************/ +/* IP method PPP */ + +static void +ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +{ + switch (status) { + case NM_PPP_STATUS_DISCONNECT: + g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); + break; + case NM_PPP_STATUS_DEAD: + g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_FAILED); + break; + default: + break; + } +} + +static void +ppp_ip4_config (NMPPPManager *ppp_manager, + const char *iface, + NMIP4Config *config, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + guint32 i, num; + guint32 bad_dns1 = htonl (0x0A0B0C0D); + guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */ + guint32 bad_dns2 = htonl (0x0A0B0C0E); + guint32 good_dns2 = htonl (0x04020202); /* GTE nameserver */ + gboolean dns_workaround = FALSE; + + /* Notify about the new data port to use */ + g_free (priv->ppp_iface); + priv->ppp_iface = g_strdup (iface); + g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT); + + /* Work around a PPP bug (#1732) which causes many mobile broadband + * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers. + * Apparently fixed in ppp-2.4.5 but we've had some reports that this is + * not the case. + * + * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=2e09ef6886bbf00bc5a9a641110f801e372ffde6 + * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=f8191bf07df374f119a07910a79217c7618f113e + */ + + num = nm_ip4_config_get_num_nameservers (config); + if (num == 2) { + gboolean found1 = FALSE, found2 = FALSE; + + for (i = 0; i < num; i++) { + guint32 ns = nm_ip4_config_get_nameserver (config, i); + + if (ns == bad_dns1) + found1 = TRUE; + else if (ns == bad_dns2) + found2 = TRUE; + } + + /* Be somewhat conservative about substitutions; the "bad" nameservers + * could actually be valid in some cases, so only substitute if ppp + * returns *only* the two bad nameservers. + */ + dns_workaround = (found1 && found2); + } + + if (!num || dns_workaround) { + nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers"); + nm_ip4_config_reset_nameservers (config); + nm_ip4_config_add_nameserver (config, good_dns1); + nm_ip4_config_add_nameserver (config, good_dns2); + } + + g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, config, NULL); +} + +static void +ppp_stats (NMPPPManager *ppp_manager, + guint32 in_bytes, + guint32 out_bytes, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { + priv->in_bytes = in_bytes; + priv->out_bytes = out_bytes; + + g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes); + } +} + +static NMActStageReturn +ppp_stage3_ip4_config_start (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + const char *ppp_name = NULL; + GError *error = NULL; + NMActStageReturn ret; + guint ip_timeout = 20; + + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (NM_MODEM_GET_CLASS (self)->get_user_pass) { + NMConnection *connection = nm_act_request_get_connection (req); + + g_assert (connection); + if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &ppp_name, NULL)) + return NM_ACT_STAGE_RETURN_FAILURE; + } + + /* Check if ModemManager requested a specific IP timeout to be used. If 0 reported, + * use the default one (20s) */ + if (priv->mm_ip_timeout > 0) { + nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds", + priv->mm_ip_timeout); + ip_timeout = priv->mm_ip_timeout; + } + + priv->ppp_manager = nm_ppp_manager_new (priv->data_port); + if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, ip_timeout, &error)) { + g_signal_connect (priv->ppp_manager, "state-changed", + G_CALLBACK (ppp_state_changed), + self); + g_signal_connect (priv->ppp_manager, "ip4-config", + G_CALLBACK (ppp_ip4_config), + self); + g_signal_connect (priv->ppp_manager, "stats", + G_CALLBACK (ppp_stats), + self); + + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + + *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + + return ret; +} + +/*****************************************************************************/ + +NMActStageReturn +nm_modem_stage3_ip4_config_start (NMModem *self, + NMDevice *device, + NMDeviceClass *device_class, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv; + NMActRequest *req; + NMActStageReturn ret; + + g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE); + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + req = nm_device_get_act_request (device); + g_assert (req); + + priv = NM_MODEM_GET_PRIVATE (self); + switch (priv->ip_method) { + case MM_MODEM_IP_METHOD_PPP: + ret = ppp_stage3_ip4_config_start (self, req, reason); + break; + case MM_MODEM_IP_METHOD_STATIC: + ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason); + break; + case MM_MODEM_IP_METHOD_DHCP: + ret = device_class->act_stage3_ip4_config_start (device, NULL, reason); + break; + default: + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); + ret = NM_ACT_STAGE_RETURN_FAILURE; + break; + } + + return ret; +} + +void +nm_modem_ip4_pre_commit (NMModem *modem, + NMDevice *device, + NMIP4Config *config) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (modem); + + /* If the modem has an ethernet-type data interface (ie, not PPP and thus + * not point-to-point) and IP config has a /32 prefix, then we assume that + * ARP will be pointless and we turn it off. + */ + if ( priv->ip_method == MM_MODEM_IP_METHOD_STATIC + || priv->ip_method == MM_MODEM_IP_METHOD_DHCP) { + const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0); + + g_assert (address); + if (address->plen == 32) + nm_platform_link_set_noarp (nm_device_get_ip_ifindex (device)); + } +} + +/*****************************************************************************/ + +NMActStageReturn +nm_modem_stage3_ip6_config_start (NMModem *self, + NMDevice *device, + NMDeviceClass *device_class, + NMDeviceStateReason *reason) +{ + /* FIXME: We don't support IPv6 on modems quite yet... */ + nm_device_activate_schedule_ip6_config_timeout (device); + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ + +static void +cancel_get_secrets (NMModem *self) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (priv->secrets_id) { + nm_act_request_cancel_secrets (priv->act_request, priv->secrets_id); + priv->secrets_id = 0; + } +} + +static void +modem_secrets_cb (NMActRequest *req, + guint32 call_id, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + g_return_if_fail (call_id == priv->secrets_id); + + priv->secrets_id = 0; + + if (error) + nm_log_warn (LOGD_MB, "%s", error->message); + + g_signal_emit (self, signals[AUTH_RESULT], 0, error); +} + +gboolean +nm_modem_get_secrets (NMModem *self, + const char *setting_name, + gboolean request_new, + const char *hint) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; + + cancel_get_secrets (self); + + if (request_new) + flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW; + priv->secrets_id = nm_act_request_get_secrets (priv->act_request, + setting_name, + flags, + hint, + modem_secrets_cb, + self); + if (priv->secrets_id) + g_signal_emit (self, signals[AUTH_REQUESTED], 0); + + return !!(priv->secrets_id); +} + +/*****************************************************************************/ + +static NMActStageReturn +act_stage1_prepare (NMModem *modem, + NMConnection *connection, + NMDeviceStateReason *reason) +{ + *reason = NM_DEVICE_STATE_REASON_UNKNOWN; + return NM_ACT_STAGE_RETURN_FAILURE; +} + +NMActStageReturn +nm_modem_act_stage1_prepare (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMActStageReturn ret; + GPtrArray *hints = NULL; + const char *setting_name = NULL; + NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; + NMConnection *connection; + + if (priv->act_request) + g_object_unref (priv->act_request); + priv->act_request = g_object_ref (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + setting_name = nm_connection_need_secrets (connection, &hints); + if (!setting_name) { + /* Ready to connect */ + g_assert (!hints); + return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, reason); + } + + /* Secrets required... */ + if (priv->secrets_tries++) + flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW; + + priv->secrets_id = nm_act_request_get_secrets (req, + setting_name, + flags, + hints ? g_ptr_array_index (hints, 0) : NULL, + modem_secrets_cb, + self); + if (priv->secrets_id) { + g_signal_emit (self, signals[AUTH_REQUESTED], 0); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + + if (hints) + g_ptr_array_free (hints, TRUE); + + return ret; +} + +/*****************************************************************************/ + +NMActStageReturn +nm_modem_act_stage2_config (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + /* Clear secrets tries counter since secrets were successfully used + * already if we get here. + */ + priv->secrets_tries = 0; + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +/*****************************************************************************/ + +gboolean +nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection) +{ + if (NM_MODEM_GET_CLASS (self)->check_connection_compatible) + return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection); + return FALSE; +} + +/*****************************************************************************/ + +gboolean +nm_modem_complete_connection (NMModem *self, + NMConnection *connection, + const GSList *existing_connections, + GError **error) +{ + if (NM_MODEM_GET_CLASS (self)->complete_connection) + return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error); + return FALSE; +} + +/*****************************************************************************/ + +static void +deactivate (NMModem *self, NMDevice *device) +{ + NMModemPrivate *priv; + int ifindex; + + g_return_if_fail (NM_IS_MODEM (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_MODEM_GET_PRIVATE (self); + + priv->secrets_tries = 0; + + if (priv->act_request) { + cancel_get_secrets (self); + g_object_unref (priv->act_request); + priv->act_request = NULL; + } + + priv->in_bytes = priv->out_bytes = 0; + + if (priv->ppp_manager) { + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + } + + switch (priv->ip_method) { + case MM_MODEM_IP_METHOD_PPP: + break; + case MM_MODEM_IP_METHOD_STATIC: + case MM_MODEM_IP_METHOD_DHCP: + ifindex = nm_device_get_ip_ifindex (device); + if (ifindex > 0) { + nm_platform_route_flush (ifindex); + nm_platform_address_flush (ifindex); + nm_platform_link_set_down (ifindex); + } + break; + default: + nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); + break; + } + + g_free (priv->ppp_iface); + priv->ppp_iface = NULL; +} + +/*****************************************************************************/ + +void +nm_modem_deactivate (NMModem *self, NMDevice *device) +{ + NM_MODEM_GET_CLASS (self)->deactivate (self, device); +} + +/*****************************************************************************/ + +void +nm_modem_device_state_changed (NMModem *self, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + gboolean was_connected = FALSE, warn = TRUE; + NMModemPrivate *priv; + + g_return_if_fail (NM_IS_MODEM (self)); + + if (old_state >= NM_DEVICE_STATE_PREPARE && old_state <= NM_DEVICE_STATE_DEACTIVATING) + was_connected = TRUE; + + priv = NM_MODEM_GET_PRIVATE (self); + + /* Make sure we don't leave the serial device open */ + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_FAILED: + if (priv->act_request) { + cancel_get_secrets (self); + g_object_unref (priv->act_request); + priv->act_request = NULL; + } + + if (was_connected) { + /* Don't bother warning on FAILED since the modem is already gone */ + if (new_state == NM_DEVICE_STATE_FAILED) + warn = FALSE; + NM_MODEM_GET_CLASS (self)->disconnect (self, warn); + } + break; + default: + break; + } +} + +/*****************************************************************************/ + +const char * +nm_modem_get_uid (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->uid; +} + +const char * +nm_modem_get_path (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->path; +} + +const char * +nm_modem_get_driver (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->driver; +} + +const char * +nm_modem_get_control_port (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->control_port; +} + +const char * +nm_modem_get_data_port (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + /* The ppp_iface takes precedence over the data interface when PPP is used, + * since data_iface is the TTY over which PPP is run, and that TTY can't + * do IP. The caller really wants the thing that's doing IP. + */ + return NM_MODEM_GET_PRIVATE (self)->ppp_iface ? + NM_MODEM_GET_PRIVATE (self)->ppp_iface : NM_MODEM_GET_PRIVATE (self)->data_port; +} + +gboolean +nm_modem_owns_port (NMModem *self, const char *iface) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + g_return_val_if_fail (iface != NULL, FALSE); + + if (NM_MODEM_GET_CLASS (self)->owns_port) + return NM_MODEM_GET_CLASS (self)->owns_port (self, iface); + + /* Fall back to data/control ports */ + if (priv->ppp_iface && (strcmp (priv->ppp_iface, iface) == 0)) + return TRUE; + if (priv->data_port && (strcmp (priv->data_port, iface) == 0)) + return TRUE; + if (priv->control_port && (strcmp (priv->control_port, iface) == 0)) + return TRUE; + + return FALSE; +} + +/*****************************************************************************/ + +void +nm_modem_get_capabilities (NMModem *self, + NMDeviceModemCapabilities *modem_caps, + NMDeviceModemCapabilities *current_caps) +{ + g_return_if_fail (NM_IS_MODEM (self)); + + NM_MODEM_GET_CLASS (self)->get_capabilities (self, modem_caps, current_caps); +} + +/*****************************************************************************/ + +static void +nm_modem_init (NMModem *self) +{ +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMModemPrivate *priv; + + object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_MODEM_GET_PRIVATE (object); + + if (!priv->data_port && !priv->control_port) { + nm_log_err (LOGD_HW, "neither modem command nor data interface provided"); + goto err; + } + + if (!priv->path) { + nm_log_err (LOGD_HW, "D-Bus path not provided"); + goto err; + } + + return object; + + err: + g_object_unref (object); + return NULL; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + break; + case PROP_DRIVER: + g_value_set_string (value, priv->driver); + break; + case PROP_CONTROL_PORT: + g_value_set_string (value, priv->control_port); + break; + case PROP_DATA_PORT: + g_value_set_string (value, nm_modem_get_data_port (NM_MODEM (object))); + break; + case PROP_UID: + g_value_set_string (value, priv->uid); + break; + case PROP_IP_METHOD: + g_value_set_uint (value, priv->ip_method); + break; + case PROP_IP_TIMEOUT: + g_value_set_uint (value, priv->mm_ip_timeout); + break; + case PROP_STATE: + g_value_set_enum (value, priv->state); + break; + case PROP_DEVICE_ID: + g_value_set_string (value, priv->device_id); + break; + case PROP_SIM_ID: + g_value_set_string (value, priv->sim_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + /* Construct only */ + priv->path = g_value_dup_string (value); + break; + case PROP_DRIVER: + /* Construct only */ + priv->driver = g_value_dup_string (value); + break; + case PROP_CONTROL_PORT: + priv->control_port = g_value_dup_string (value); + break; + case PROP_DATA_PORT: + priv->data_port = g_value_dup_string (value); + break; + case PROP_UID: + /* Construct only */ + priv->uid = g_value_dup_string (value); + break; + case PROP_IP_METHOD: + priv->ip_method = g_value_get_uint (value); + break; + case PROP_IP_TIMEOUT: + priv->mm_ip_timeout = g_value_get_uint (value); + break; + case PROP_STATE: + priv->state = g_value_get_enum (value); + break; + case PROP_DEVICE_ID: + /* construct only */ + priv->device_id = g_value_dup_string (value); + break; + case PROP_SIM_ID: + g_free (priv->sim_id); + priv->sim_id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + if (priv->act_request) { + g_object_unref (priv->act_request); + priv->act_request = NULL; + } + + G_OBJECT_CLASS (nm_modem_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + g_free (priv->uid); + g_free (priv->path); + g_free (priv->driver); + g_free (priv->control_port); + g_free (priv->data_port); + g_free (priv->device_id); + g_free (priv->sim_id); + + G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object); +} + +static void +nm_modem_class_init (NMModemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + klass->act_stage1_prepare = act_stage1_prepare; + klass->deactivate = deactivate; + + /* Properties */ + + g_object_class_install_property + (object_class, PROP_UID, + g_param_spec_string (NM_MODEM_UID, + "UID", + "Modem unique ID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_MODEM_PATH, + "DBus path", + "DBus path", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_DRIVER, + g_param_spec_string (NM_MODEM_DRIVER, + "Driver", + "Driver", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_CONTROL_PORT, + g_param_spec_string (NM_MODEM_CONTROL_PORT, + "Control port", + "The port controlling the modem", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_DATA_PORT, + g_param_spec_string (NM_MODEM_DATA_PORT, + "Data port", + "The port to connect to", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (object_class, PROP_IP_METHOD, + g_param_spec_uint (NM_MODEM_IP_METHOD, + "IP method", + "IP method", + MM_MODEM_IP_METHOD_PPP, + MM_MODEM_IP_METHOD_DHCP, + MM_MODEM_IP_METHOD_PPP, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_IP_TIMEOUT, + g_param_spec_uint (NM_MODEM_IP_TIMEOUT, + "IP timeout", + "IP timeout", + 0, 360, 20, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_enum (NM_MODEM_STATE, + "State", + "State", + NM_TYPE_MODEM_STATE, + NM_MODEM_STATE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_DEVICE_ID, + g_param_spec_string (NM_MODEM_DEVICE_ID, + "DeviceId", + "Device ID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_SIM_ID, + g_param_spec_string (NM_MODEM_SIM_ID, + "SimId", + "Sim ID", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + /* Signals */ + + signals[PPP_STATS] = + g_signal_new ("ppp-stats", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ppp_stats), + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[PPP_FAILED] = + g_signal_new ("ppp-failed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ppp_failed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[IP4_CONFIG_RESULT] = + g_signal_new (NM_MODEM_IP4_CONFIG_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ip4_config_result), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_POINTER); + + signals[PREPARE_RESULT] = + g_signal_new (NM_MODEM_PREPARE_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, prepare_result), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT); + + signals[AUTH_REQUESTED] = + g_signal_new (NM_MODEM_AUTH_REQUESTED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, auth_requested), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[AUTH_RESULT] = + g_signal_new (NM_MODEM_AUTH_RESULT, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, auth_result), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[REMOVED] = + g_signal_new (NM_MODEM_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, removed), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[STATE_CHANGED] = + g_signal_new (NM_MODEM_STATE_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 2, NM_TYPE_MODEM_STATE, NM_TYPE_MODEM_STATE); + + dbus_g_error_domain_register (NM_MODEM_ERROR, + NM_DBUS_INTERFACE_DEVICE_MODEM, + NM_TYPE_MODEM_ERROR); +} diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h new file mode 100644 index 00000000..c992cf72 --- /dev/null +++ b/src/devices/wwan/nm-modem.h @@ -0,0 +1,217 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2009 - 2011 Red Hat, Inc. + * Copyright (C) 2009 Novell, Inc. + */ + +#ifndef NM_MODEM_H +#define NM_MODEM_H + +#include <dbus/dbus-glib.h> +#include <glib-object.h> +#include "ppp-manager/nm-ppp-manager.h" +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM (nm_modem_get_type ()) +#define NM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM, NMModem)) +#define NM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM, NMModemClass)) +#define NM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM)) +#define NM_IS_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM)) +#define NM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM, NMModemClass)) + +/* Properties */ +#define NM_MODEM_UID "uid" +#define NM_MODEM_PATH "path" +#define NM_MODEM_DRIVER "driver" +#define NM_MODEM_CONTROL_PORT "control-port" +#define NM_MODEM_DATA_PORT "data-port" +#define NM_MODEM_IP_METHOD "ip-method" +#define NM_MODEM_IP_TIMEOUT "ip-timeout" +#define NM_MODEM_STATE "state" +#define NM_MODEM_DEVICE_ID "device-id" +#define NM_MODEM_SIM_ID "sim-id" + +/* Signals */ +#define NM_MODEM_PPP_STATS "ppp-stats" +#define NM_MODEM_PPP_FAILED "ppp-failed" +#define NM_MODEM_PREPARE_RESULT "prepare-result" +#define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result" +#define NM_MODEM_AUTH_REQUESTED "auth-requested" +#define NM_MODEM_AUTH_RESULT "auth-result" +#define NM_MODEM_REMOVED "removed" +#define NM_MODEM_STATE_CHANGED "state-changed" + +#define MM_MODEM_IP_METHOD_PPP 0 +#define MM_MODEM_IP_METHOD_STATIC 1 +#define MM_MODEM_IP_METHOD_DHCP 2 + +typedef enum { + NM_MODEM_ERROR_CONNECTION_NOT_GSM, /*< nick=ConnectionNotGsm >*/ + NM_MODEM_ERROR_CONNECTION_NOT_CDMA, /*< nick=ConnectionNotCdma >*/ + NM_MODEM_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ + NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ + NM_MODEM_ERROR_INITIALIZATION_FAILED, /*< nick=InitializationFailed >*/ +} NMModemError; + +typedef enum { /*< underscore_name=nm_modem_state >*/ + NM_MODEM_STATE_UNKNOWN = 0, + NM_MODEM_STATE_FAILED = 1, + NM_MODEM_STATE_INITIALIZING = 2, + NM_MODEM_STATE_LOCKED = 3, + NM_MODEM_STATE_DISABLED = 4, + NM_MODEM_STATE_DISABLING = 5, + NM_MODEM_STATE_ENABLING = 6, + NM_MODEM_STATE_ENABLED = 7, + NM_MODEM_STATE_SEARCHING = 8, + NM_MODEM_STATE_REGISTERED = 9, + NM_MODEM_STATE_DISCONNECTING = 10, + NM_MODEM_STATE_CONNECTING = 11, + NM_MODEM_STATE_CONNECTED = 12, +} NMModemState; + +#define NM_MODEM_ERROR (nm_modem_error_quark ()) +GQuark nm_modem_error_quark (void); + + +typedef struct { + GObject parent; +} NMModem; + +typedef struct { + GObjectClass parent; + + void (*get_capabilities) (NMModem *self, + NMDeviceModemCapabilities *modem_caps, + NMDeviceModemCapabilities *current_caps); + + gboolean (*get_user_pass) (NMModem *modem, + NMConnection *connection, + const char **user, + const char **pass); + + gboolean (*check_connection_compatible) (NMModem *modem, + NMConnection *connection); + + gboolean (*complete_connection) (NMModem *modem, + NMConnection *connection, + const GSList *existing_connections, + GError **error); + + NMActStageReturn (*act_stage1_prepare) (NMModem *modem, + NMConnection *connection, + NMDeviceStateReason *reason); + + NMActStageReturn (*static_stage3_ip4_config_start) (NMModem *self, + NMActRequest *req, + NMDeviceStateReason *reason); + + void (*set_mm_enabled) (NMModem *self, gboolean enabled); + + void (*disconnect) (NMModem *self, gboolean warn); + + void (*deactivate) (NMModem *self, NMDevice *device); + + gboolean (*owns_port) (NMModem *self, const char *iface); + + /* Signals */ + void (*ppp_stats) (NMModem *self, guint32 in_bytes, guint32 out_bytes); + void (*ppp_failed) (NMModem *self, NMDeviceStateReason reason); + + void (*prepare_result) (NMModem *self, gboolean success, NMDeviceStateReason reason); + void (*ip4_config_result) (NMModem *self, NMIP4Config *config, GError *error); + + void (*auth_requested) (NMModem *self); + void (*auth_result) (NMModem *self, GError *error); + + void (*state_changed) (NMModem *self, + NMModemState new_state, + NMModemState old_state); + + void (*removed) (NMModem *self); +} NMModemClass; + +GType nm_modem_get_type (void); + +const char *nm_modem_get_path (NMModem *modem); +const char *nm_modem_get_uid (NMModem *modem); +const char *nm_modem_get_control_port (NMModem *modem); +const char *nm_modem_get_data_port (NMModem *modem); +const char *nm_modem_get_driver (NMModem *modem); + +gboolean nm_modem_owns_port (NMModem *modem, const char *iface); + +void nm_modem_get_capabilities (NMModem *self, + NMDeviceModemCapabilities *modem_caps, + NMDeviceModemCapabilities *current_caps); + +gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection); + +gboolean nm_modem_complete_connection (NMModem *self, + NMConnection *connection, + const GSList *existing_connections, + GError **error); + +NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem, + NMActRequest *req, + NMDeviceStateReason *reason); + +NMActStageReturn nm_modem_act_stage2_config (NMModem *modem, + NMActRequest *req, + NMDeviceStateReason *reason); + +NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem, + NMDevice *device, + NMDeviceClass *device_class, + NMDeviceStateReason *reason); + +NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem, + NMDevice *device, + NMDeviceClass *device_class, + NMDeviceStateReason *reason); + +void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config); + +gboolean nm_modem_get_secrets (NMModem *modem, + const char *setting_name, + gboolean request_new, + const char *hint); + +void nm_modem_deactivate (NMModem *modem, NMDevice *device); + +void nm_modem_device_state_changed (NMModem *modem, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason); + +void nm_modem_set_mm_enabled (NMModem *self, gboolean enabled); + +NMModemState nm_modem_get_state (NMModem *self); +void nm_modem_set_state (NMModem *self, + NMModemState new_state, + const char *reason); +void nm_modem_set_prev_state (NMModem *self, const char *reason); +const char * nm_modem_state_to_string (NMModemState state); + +/* For the modem-manager only */ +void nm_modem_emit_removed (NMModem *self); + +G_END_DECLS + +#endif /* NM_MODEM_H */ diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c new file mode 100644 index 00000000..b1e2307e --- /dev/null +++ b/src/devices/wwan/nm-wwan-factory.c @@ -0,0 +1,136 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> + +#include "config.h" +#include "nm-device-factory.h" +#include "nm-wwan-factory.h" +#include "nm-modem-manager.h" +#include "nm-device-modem.h" +#include "nm-logging.h" + +static GType nm_wwan_factory_get_type (void); + +static void device_factory_interface_init (NMDeviceFactory *factory_iface); + +G_DEFINE_TYPE_EXTENDED (NMWwanFactory, nm_wwan_factory, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init)) + +#define NM_WWAN_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_WWAN_FACTORY, NMWwanFactoryPrivate)) + +typedef struct { + NMModemManager *mm; +} NMWwanFactoryPrivate; + +/************************************************************************/ + +#define PLUGIN_TYPE NM_DEVICE_TYPE_MODEM + +G_MODULE_EXPORT NMDeviceFactory * +nm_device_factory_create (GError **error) +{ + return (NMDeviceFactory *) g_object_new (NM_TYPE_WWAN_FACTORY, NULL); +} + +G_MODULE_EXPORT NMDeviceType +nm_device_factory_get_device_type (void) +{ + return PLUGIN_TYPE; +} + +/************************************************************************/ + +static void +modem_added_cb (NMModemManager *manager, + NMModem *modem, + gpointer user_data) +{ + NMWwanFactory *self = NM_WWAN_FACTORY (user_data); + NMDevice *device; + const char *driver, *port; + + /* Do nothing if the modem was consumed by some other plugin */ + if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem))) + return; + + driver = nm_modem_get_driver (modem); + + /* If it was a Bluetooth modem and no bluetooth device claimed it, ignore + * it. The rfcomm port (and thus the modem) gets created automatically + * by the Bluetooth code during the connection process. + */ + if (driver && strstr (driver, "bluetooth")) { + port = nm_modem_get_data_port (modem); + if (!port) + port = nm_modem_get_control_port (modem); + nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", port); + return; + } + + /* Make the new modem device */ + device = nm_device_modem_new (modem); + g_assert (device); + g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device); + g_object_unref (device); +} + +static void +nm_wwan_factory_init (NMWwanFactory *self) +{ + NMWwanFactoryPrivate *priv = NM_WWAN_FACTORY_GET_PRIVATE (self); + + priv->mm = g_object_new (NM_TYPE_MODEM_MANAGER, NULL); + g_assert (priv->mm); + g_signal_connect (priv->mm, + NM_MODEM_MANAGER_MODEM_ADDED, + G_CALLBACK (modem_added_cb), + self); +} + +static void +device_factory_interface_init (NMDeviceFactory *factory_iface) +{ +} + +static void +dispose (GObject *object) +{ + NMWwanFactory *self = NM_WWAN_FACTORY (object); + NMWwanFactoryPrivate *priv = NM_WWAN_FACTORY_GET_PRIVATE (self); + + if (priv->mm) + g_signal_handlers_disconnect_by_func (priv->mm, modem_added_cb, self); + g_clear_object (&priv->mm); + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_wwan_factory_parent_class)->dispose (object); +} + +static void +nm_wwan_factory_class_init (NMWwanFactoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMWwanFactoryPrivate)); + + object_class->dispose = dispose; +} diff --git a/src/devices/wwan/nm-wwan-factory.h b/src/devices/wwan/nm-wwan-factory.h new file mode 100644 index 00000000..b7aee01f --- /dev/null +++ b/src/devices/wwan/nm-wwan-factory.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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. + * + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef NM_WWAN_FACTORY_H +#define NM_WWAN_FACTORY_H + +#include <glib-object.h> + +#define NM_TYPE_WWAN_FACTORY (nm_wwan_factory_get_type ()) +#define NM_WWAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WWAN_FACTORY, NMWwanFactory)) + +typedef struct { + GObject parent; +} NMWwanFactory; + +typedef struct { + GObjectClass parent; +} NMWwanFactoryClass; + +#endif /* NM_WWAN_FACTORY_H */ diff --git a/src/devices/wwan/wwan-exports.ver b/src/devices/wwan/wwan-exports.ver new file mode 100644 index 00000000..dc505d17 --- /dev/null +++ b/src/devices/wwan/wwan-exports.ver @@ -0,0 +1,28 @@ +{ +global: + nm_modem_act_stage1_prepare; + nm_modem_act_stage2_config; + nm_modem_check_connection_compatible; + nm_modem_complete_connection; + nm_modem_deactivate; + nm_modem_device_state_changed; + nm_modem_error_quark; + nm_modem_get_capabilities; + nm_modem_get_control_port; + nm_modem_get_data_port; + nm_modem_get_driver; + nm_modem_get_path; + nm_modem_get_secrets; + nm_modem_get_state; + nm_modem_get_type; + nm_modem_get_uid; + nm_modem_ip4_pre_commit; + nm_modem_manager_get_type; + nm_modem_owns_port; + nm_modem_set_mm_enabled; + nm_modem_stage3_ip4_config_start; + nm_modem_stage3_ip6_config_start; + nm_modem_state_to_string; +local: + *; +}; |