diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:38:15 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:38:15 +0100 |
commit | 87bd9deec22af69bb27226254803ac5c63b18d78 (patch) | |
tree | c34d42bf75c20b3fd740e4cd59e45aa6901a9fed /plugins |
Imported Upstream version 0.3upstream/0.3
Diffstat (limited to 'plugins')
56 files changed, 8588 insertions, 0 deletions
diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules new file mode 100644 index 0000000..71dc6b8 --- /dev/null +++ b/plugins/77-mm-ericsson-mbm.rules @@ -0,0 +1,41 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add|change", GOTO="mm_mbm_end" +SUBSYSTEM!="usb", GOTO="mm_mbm_end" +ENV{DEVTYPE}!="usb_device", GOTO="mm_mbm_end" + +# Ericsson F3507g +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{ID_MM_ERICSSON_MBM}="1" + +# Ericsson F3607gw +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{ID_MM_ERICSSON_MBM}="1" + +# Ericsson F3307 +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190a", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{ID_MM_ERICSSON_MBM}="1" + +# Ericsson C3607w +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{ID_MM_ERICSSON_MBM}="1" + +# Sony-Ericsson MD300 +ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1" + +# Dell 5530 HSDPA +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1" + +# Dell F3607gw +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1" + +# Toshiba +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1" + +# Toshiba F3607gw +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{ID_MM_ERICSSON_MBM}="1" + +LABEL="mm_mbm_end" + diff --git a/plugins/77-mm-longcheer-port-types.rules b/plugins/77-mm-longcheer-port-types.rules new file mode 100644 index 0000000..7317df7 --- /dev/null +++ b/plugins/77-mm-longcheer-port-types.rules @@ -0,0 +1,45 @@ +# do not edit this file, it will be overwritten on update + +# Longcheer makes modules that other companies rebrand, like: +# +# Alcatel One Touch X020 +# Alcatel One Touch X030 +# MobiData MBD-200HU +# ST Mobile Connect HSUPA USB Modem + + +ACTION!="add|change", GOTO="mm_longcheer_port_types_end" +SUBSYSTEM!="tty", GOTO="mm_longcheer_port_types_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1c9e", GOTO="mm_longcheer_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bbb", GOTO="mm_tamobile_vendorcheck" +GOTO="mm_longcheer_port_types_end" + +LABEL="mm_longcheer_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="6060", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +# Alcatel One Touch X020 +ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="6061", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +GOTO="mm_longcheer_port_types_end" + + +LABEL="mm_tamobile_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +# Alcatel One Touch X060s +ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="0000", ENV{ID_MM_LONGCHEER_TAGGED}="1" + +GOTO="mm_longcheer_port_types_end" + + +LABEL="mm_longcheer_port_types_end" + diff --git a/plugins/77-mm-zte-port-types.rules b/plugins/77-mm-zte-port-types.rules new file mode 100644 index 0000000..9d64aa9 --- /dev/null +++ b/plugins/77-mm-zte-port-types.rules @@ -0,0 +1,115 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add|change", GOTO="mm_zte_port_types_end" +SUBSYSTEM!="tty", GOTO="mm_zte_port_types_end" + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="19d2", GOTO="mm_zte_port_types_vendorcheck" +GOTO="mm_zte_port_types_end" + +LABEL="mm_zte_port_types_vendorcheck" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1" + +LABEL="mm_zte_port_types_end" + diff --git a/plugins/Makefile.am b/plugins/Makefile.am new file mode 100644 index 0000000..a361358 --- /dev/null +++ b/plugins/Makefile.am @@ -0,0 +1,268 @@ +pkglib_LTLIBRARIES = \ + libmm-plugin-generic.la \ + libmm-plugin-moto-c.la \ + libmm-plugin-gobi.la \ + libmm-plugin-huawei.la \ + libmm-plugin-hso.la \ + libmm-plugin-option.la \ + libmm-plugin-sierra.la \ + libmm-plugin-novatel.la \ + libmm-plugin-nokia.la \ + libmm-plugin-zte.la \ + libmm-plugin-mbm.la \ + libmm-plugin-longcheer.la \ + libmm-plugin-anydata.la + +# Generic + +libmm_plugin_generic_la_SOURCES = \ + mm-plugin-generic.c \ + mm-plugin-generic.h + +libmm_plugin_generic_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_generic_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Motorola C-series phones + +libmm_plugin_moto_c_la_SOURCES = \ + mm-plugin-moto-c.c \ + mm-plugin-moto-c.h \ + mm-modem-moto-c-gsm.c \ + mm-modem-moto-c-gsm.h + +libmm_plugin_moto_c_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_moto_c_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Qualcomm Gobi + +libmm_plugin_gobi_la_SOURCES = \ + mm-plugin-gobi.c \ + mm-plugin-gobi.h \ + mm-modem-gobi-gsm.c \ + mm-modem-gobi-gsm.h + +libmm_plugin_gobi_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_gobi_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Huawei + +libmm_plugin_huawei_la_SOURCES = \ + mm-plugin-huawei.c \ + mm-plugin-huawei.h \ + mm-modem-huawei-gsm.c \ + mm-modem-huawei-gsm.h \ + mm-modem-huawei-cdma.c \ + mm-modem-huawei-cdma.h + +libmm_plugin_huawei_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_huawei_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# HSO + +libmm_plugin_hso_la_SOURCES = \ + mm-plugin-hso.c \ + mm-plugin-hso.h \ + mm-modem-gsm-hso-glue.h \ + mm-modem-hso.c \ + mm-modem-hso.h + +mm-modem-gsm-hso-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-hso.xml + dbus-binding-tool --prefix=mm_modem_gsm_hso --mode=glib-server --output=$@ $< + +libmm_plugin_hso_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_hso_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# MBM + +libmm_plugin_mbm_la_SOURCES = \ + mm-plugin-mbm.c \ + mm-plugin-mbm.h \ + mm-modem-mbm.c \ + mm-modem-mbm.h + +libmm_plugin_mbm_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_mbm_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Option + +libmm_plugin_option_la_SOURCES = \ + mm-plugin-option.c \ + mm-plugin-option.h \ + mm-modem-option.c \ + mm-modem-option.h + +libmm_plugin_option_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_option_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Sierra + +libmm_plugin_sierra_la_SOURCES = \ + mm-plugin-sierra.c \ + mm-plugin-sierra.h \ + mm-modem-sierra-gsm.c \ + mm-modem-sierra-gsm.h \ + mm-modem-sierra-cdma.c \ + mm-modem-sierra-cdma.h + +libmm_plugin_sierra_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_sierra_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Novatel + +libmm_plugin_novatel_la_SOURCES = \ + mm-plugin-novatel.c \ + mm-plugin-novatel.h \ + mm-modem-novatel-gsm.c \ + mm-modem-novatel-gsm.h + +libmm_plugin_novatel_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_novatel_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Nokia + +libmm_plugin_nokia_la_SOURCES = \ + mm-plugin-nokia.c \ + mm-plugin-nokia.h \ + mm-modem-nokia.c \ + mm-modem-nokia.h + +libmm_plugin_nokia_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_nokia_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Zte + +libmm_plugin_zte_la_SOURCES = \ + mm-plugin-zte.c \ + mm-plugin-zte.h \ + mm-modem-zte.c \ + mm-modem-zte.h + +libmm_plugin_zte_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_zte_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# Longcheer (and rebranded dongles) + +libmm_plugin_longcheer_la_SOURCES = \ + mm-plugin-longcheer.c \ + mm-plugin-longcheer.h + +libmm_plugin_longcheer_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_longcheer_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + +# AnyData CDMA + +libmm_plugin_anydata_la_SOURCES = \ + mm-plugin-anydata.c \ + mm-plugin-anydata.h \ + mm-modem-anydata-cdma.c \ + mm-modem-anydata-cdma.h + +libmm_plugin_anydata_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_anydata_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + + +udevrulesdir = $(UDEV_BASE_DIR)/rules.d +udevrules_DATA = \ + 77-mm-ericsson-mbm.rules \ + 77-mm-zte-port-types.rules \ + 77-mm-longcheer-port-types.rules + +BUILT_SOURCES = \ + mm-modem-gsm-hso-glue.h + +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = \ + $(udevrules_DATA) + diff --git a/plugins/mm-modem-anydata-cdma.c b/plugins/mm-modem-anydata-cdma.c new file mode 100644 index 0000000..f6528ec --- /dev/null +++ b/plugins/mm-modem-anydata-cdma.c @@ -0,0 +1,384 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-anydata-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemAnydataCdma, mm_modem_anydata_cdma, MM_TYPE_GENERIC_CDMA, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +MMModem * +mm_modem_anydata_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_ANYDATA_CDMA, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, + MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + MM_GENERIC_CDMA_REGISTRATION_TRY_CSS, FALSE, + NULL)); +} + +/*****************************************************************************/ + +static const char * +strip_response (const char *resp, const char *cmd) +{ + const char *p = resp; + + if (p) { + if (!strncmp (p, cmd, strlen (cmd))) + p += strlen (cmd); + while (*p == ' ') + p++; + } + return p; +} + +static gboolean +uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val) +{ + long int tmp; + char *str; + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + g_return_val_if_fail (str != NULL, FALSE); + + errno = 0; + tmp = strtol (str, NULL, 10); + if (errno == 0 && tmp >= 0 && tmp <= G_MAXUINT) { + *val = (guint32) tmp; + success = TRUE; + } + g_free (str); + return success; +} + +static gboolean +int_from_match_item (GMatchInfo *match_info, guint32 num, gint *val) +{ + long int tmp; + char *str; + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + g_return_val_if_fail (str != NULL, FALSE); + + errno = 0; + tmp = strtol (str, NULL, 10); + if (errno == 0 && tmp >= G_MININT && tmp <= G_MAXINT) { + *val = (gint) tmp; + success = TRUE; + } + g_free (str); + return success; +} + +static void +evdo_state_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdmaRegistrationState reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + const char *reply; + GRegex *r; + GMatchInfo *match_info; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) { + /* If HSTATE returned an error, assume the device is not EVDO capable + * or EVDO is not registered. + */ + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } + + mm_callback_info_schedule (info); + return; + } + + reply = strip_response (response->str, "*HSTATE:"); + + /* Format is "<at state>,<session state>,<channel>,<pn>,<EcIo>,<rssi>,..." */ + r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,\\s*([^,\\)]*)\\s*,.*", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + if (!r) { + /* Parse error; warn about it and assume EVDO is not available */ + g_warning ("AnyData(%s): failed to create EVDO state regex: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + mm_callback_info_schedule (info); + return; + } + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 6) { + guint32 val = 0; + gint dbm = 0; + + /* dBm is between -106 (worst) and -20.7 (best) */ + int_from_match_item (match_info, 6, &dbm); + + /* Parse the EVDO radio state */ + if (uint_from_match_item (match_info, 1, &val)) { + switch (val) { + case 3: /* IDLE */ + /* If IDLE and the EVDO dBm is -105 or lower, assume no service. + * It may be that IDLE actually means NO SERVICE too; not sure. + */ + if (dbm > -105) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + case 4: /* ACCESS */ + case 5: /* CONNECT */ + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + default: + g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + /* fall through */ + case 0: /* NO SERVICE */ + case 1: /* ACQUISITION */ + case 2: /* SYNC */ + break; + } + } + } + + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); + + mm_callback_info_schedule (info); +} + +static void +state_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemCdmaRegistrationState reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN; + const char *reply; + GRegex *r; + GMatchInfo *match_info; + + info->error = mm_modem_check_removed (info->modem, error); + if (info->error) { + if (info->modem) { + /* Assume if we got this far, we're registered even if an error + * occurred. We're not sure if all AnyData CDMA modems support + * the *STATE and *HSTATE commands. + */ + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } + + mm_callback_info_schedule (info); + return; + } + + reply = strip_response (response->str, "*STATE:"); + + /* Format is "<channel>,<pn>,<sid>,<nid>,<state>,<rssi>,..." */ + r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([^,\\)]*)\\s*,.*", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + if (!r) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse sysinfo results (regex creation failed)."); + mm_callback_info_schedule (info); + return; + } + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 6) { + guint32 val = 0; + gint dbm = 0; + + /* dBm is between -106 (worst) and -20.7 (best) */ + int_from_match_item (match_info, 6, &dbm); + + /* Parse the 1x radio state */ + if (uint_from_match_item (match_info, 5, &val)) { + switch (val) { + case 1: /* IDLE */ + /* If IDLE and the 1X dBm is -105 or lower, assume no service. + * It may be that IDLE actually means NO SERVICE too; not sure. + */ + if (dbm > -105) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + case 2: /* ACCESS */ + case 3: /* PAGING */ + case 4: /* TRAFFIC */ + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + break; + default: + g_message ("ANYDATA: unknown *STATE (%d); assuming no service.", val); + /* fall through */ + case 0: /* NO SERVICE */ + break; + } + } + } + + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); + + /* Try for EVDO state too */ + mm_serial_port_queue_command (port, "*HSTATE?", 3, evdo_state_done, info); +} + +static void +query_registration_state (MMGenericCdma *cdma, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary, *secondary, *port; + + port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY); + + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data); + + if (mm_port_get_connected (MM_PORT (primary))) { + if (!secondary) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot get query registration state while connected"); + mm_callback_info_schedule (info); + return; + } + + /* Use secondary port if primary is connected */ + port = secondary; + } + + mm_serial_port_queue_command (port, "*STATE?", 3, state_done, info); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMPort *port = NULL; + GRegex *regex; + + port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error); + if (port && MM_IS_SERIAL_PORT (port)) { + /* Data state notifications */ + + /* Data call has connected */ + regex = g_regex_new ("\\r\\n\\*ACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Data call disconnected */ + regex = g_regex_new ("\\r\\n\\*INACTIVE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Modem is now dormant */ + regex = g_regex_new ("\\r\\n\\*DORMANT:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Abnomral state notifications + * + * FIXME: set 1X/EVDO registration state to UNKNOWN when these + * notifications are received? + */ + + /* Network acquisition fail */ + regex = g_regex_new ("\\r\\n\\*OFFLINE:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Registration fail */ + regex = g_regex_new ("\\r\\n\\*REGREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Authentication fail */ + regex = g_regex_new ("\\r\\n\\*AUTHREQ:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_anydata_cdma_init (MMModemAnydataCdma *self) +{ +} + +static void +mm_modem_anydata_cdma_class_init (MMModemAnydataCdmaClass *klass) +{ + MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass); + + mm_modem_anydata_cdma_parent_class = g_type_class_peek_parent (klass); + + cdma_class->query_registration_state = query_registration_state; + +#if 0 + /* FIXME: maybe use AT*SLEEP=0/1 to disable/enable slotted mode for powersave */ + cdma_class->post_enable = post_enable; + cdma_class->post_enable = post_disable; +#endif +} + diff --git a/plugins/mm-modem-anydata-cdma.h b/plugins/mm-modem-anydata-cdma.h new file mode 100644 index 0000000..d2695d5 --- /dev/null +++ b/plugins/mm-modem-anydata-cdma.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#ifndef MM_MODEM_ANYDATA_CDMA_H +#define MM_MODEM_ANYDATA_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_ANYDATA_CDMA (mm_modem_anydata_cdma_get_type ()) +#define MM_MODEM_ANYDATA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_ANYDATA_CDMA, MMModemAnydataCdma)) +#define MM_MODEM_ANYDATA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_ANYDATA_CDMA, MMModemAnydataCdmaClass)) +#define MM_IS_MODEM_ANYDATA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_ANYDATA_CDMA)) +#define MM_IS_MODEM_ANYDATA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_ANYDATA_CDMA)) +#define MM_MODEM_ANYDATA_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_ANYDATA_CDMA, MMModemAnydataCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemAnydataCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemAnydataCdmaClass; + +GType mm_modem_anydata_cdma_get_type (void); + +MMModem *mm_modem_anydata_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_ANYDATA_CDMA_H */ diff --git a/plugins/mm-modem-gobi-gsm.c b/plugins/mm-modem-gobi-gsm.c new file mode 100644 index 0000000..7ea9f8f --- /dev/null +++ b/plugins/mm-modem-gobi-gsm.c @@ -0,0 +1,109 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mm-modem-gobi-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm-card.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); + +G_DEFINE_TYPE_EXTENDED (MMModemGobiGsm, mm_modem_gobi_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) + + +MMModem * +mm_modem_gobi_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_GOBI_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +get_string_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error && response && !strcmp (response->str, "ERROR")) { + info->error = g_error_new_literal (MM_MOBILE_ERROR, + MM_MOBILE_ERROR_SIM_NOT_INSERTED, + "Unable to read IMSI"); + } else if (error) + info->error = g_error_copy (error); + else + mm_callback_info_set_result (info, g_strdup (response->str), g_free); + + mm_callback_info_schedule (info); +} + +static void +get_imsi (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMSerialPort *primary; + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command_cached (primary, "+CIMI", 3, get_string_done, info); +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_imsi = get_imsi; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ +} + +static void +mm_modem_gobi_gsm_init (MMModemGobiGsm *self) +{ +} + +static void +mm_modem_gobi_gsm_class_init (MMModemGobiGsmClass *klass) +{ +} + diff --git a/plugins/mm-modem-gobi-gsm.h b/plugins/mm-modem-gobi-gsm.h new file mode 100644 index 0000000..4bd262f --- /dev/null +++ b/plugins/mm-modem-gobi-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_GOBI_GSM_H +#define MM_MODEM_GOBI_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_GOBI_GSM (mm_modem_gobi_gsm_get_type ()) +#define MM_MODEM_GOBI_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GOBI_GSM, MMModemGobiGsm)) +#define MM_MODEM_GOBI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_GOBI_GSM, MMModemGobiGsmClass)) +#define MM_IS_MODEM_GOBI_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GOBI_GSM)) +#define MM_IS_MODEM_GOBI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_GOBI_GSM)) +#define MM_MODEM_GOBI_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_GOBI_GSM, MMModemGobiGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemGobiGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemGobiGsmClass; + +GType mm_modem_gobi_gsm_get_type (void); + +MMModem *mm_modem_gobi_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_GOBI_GSM_H */ diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c new file mode 100644 index 0000000..f1295e2 --- /dev/null +++ b/plugins/mm-modem-hso.c @@ -0,0 +1,743 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <dbus/dbus-glib.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-hso.h" +#include "mm-modem-simple.h" +#include "mm-serial-parsers.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void impl_hso_authenticate (MMModemHso *self, + const char *username, + const char *password, + DBusGMethodInvocation *context); + +#include "mm-modem-gsm-hso-glue.h" + +static void modem_init (MMModem *modem_class); +static void modem_simple_init (MMModemSimple *simple_class); + +G_DEFINE_TYPE_EXTENDED (MMModemHso, mm_modem_hso, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) + +#define MM_MODEM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HSO, MMModemHsoPrivate)) + +static void _internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info); + +const char *auth_commands[] = { + "$QCPDPP", + /* Icera-based devices (GI0322/Quicksilver, iCON 505) don't implement + * $QCPDPP, but instead use _OPDPP with the same arguments. + */ + "_OPDPP", + NULL +}; + +typedef struct { + /* Pending connection attempt */ + MMCallbackInfo *connect_pending_data; + guint connect_pending_id; + + guint32 auth_idx; +} MMModemHsoPrivate; + +#define OWANDATA_TAG "_OWANDATA: " + +MMModem * +mm_modem_hso_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_HSO, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_STATIC, + NULL)); +} + +#define IGNORE_ERRORS_TAG "ignore-errors" + +static void +hso_call_control_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG)) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static guint32 +hso_get_cid (MMModemHso *self) +{ + guint32 cid; + + cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)); + if (cid == 0) + cid = 1; + + return cid; +} + +static void +hso_call_control (MMModemHso *self, + gboolean activate, + gboolean ignore_errors, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + mm_callback_info_set_data (info, IGNORE_ERRORS_TAG, GUINT_TO_POINTER (ignore_errors), NULL); + + command = g_strdup_printf ("AT_OWANCALL=%d,%d,1", hso_get_cid (self), activate ? 1 : 0); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, command, 3, hso_call_control_done, info); + g_free (command); +} + +static void +connect_pending_done (MMModemHso *self) +{ + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + + if (priv->connect_pending_data) { + mm_callback_info_schedule (priv->connect_pending_data); + priv->connect_pending_data = NULL; + } + + if (priv->connect_pending_id) { + g_source_remove (priv->connect_pending_id); + priv->connect_pending_id = 0; + } +} + +static gboolean +hso_connect_timed_out (gpointer data) +{ + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (data); + + priv->connect_pending_data->error = g_error_new_literal (MM_SERIAL_ERROR, + MM_SERIAL_RESPONSE_TIMEOUT, + "Connection timed out"); + connect_pending_done (MM_MODEM_HSO (data)); + + return FALSE; +} + +static void +hso_enabled (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } else { + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (modem); + GSource *source; + + source = g_timeout_source_new_seconds (30); + g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (hso_connect_timed_out), G_OBJECT (modem))); + g_source_attach (source, NULL); + priv->connect_pending_data = info; + priv->connect_pending_id = g_source_get_id (source); + g_source_unref (source); + } +} + +static void +clear_old_context (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } else { + /* Success, activate the PDP context and start the data session */ + hso_call_control (MM_MODEM_HSO (modem), TRUE, FALSE, hso_enabled, info); + } +} + +static void +auth_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHso *self = MM_MODEM_HSO (info->modem); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + + if (error) { + priv->auth_idx++; + if (auth_commands[priv->auth_idx]) { + /* Try the next auth command */ + _internal_hso_modem_authenticate (self, info); + } else { + /* Reset to 0 so that something gets tried for the next connection */ + priv->auth_idx = 0; + + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } + } else { + priv->auth_idx = 0; + + /* success, kill any existing connections first */ + hso_call_control (self, FALSE, TRUE, clear_old_context, info); + } +} + +static void +_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info) +{ + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + MMSerialPort *primary; + guint32 cid; + char *command; + const char *username, *password; + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + cid = hso_get_cid (self); + + username = mm_callback_info_get_data (info, "username"); + password = mm_callback_info_get_data (info, "password"); + + if (!username && !password) + command = g_strdup_printf ("%s=%d,0", auth_commands[priv->auth_idx], cid); + else { + command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"", + auth_commands[priv->auth_idx], + cid, + password ? password : "", + username ? username : ""); + + } + + mm_serial_port_queue_command (primary, command, 3, auth_done, info); + g_free (command); +} + +void +mm_hso_modem_authenticate (MMModemHso *self, + const char *username, + const char *password, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + g_return_if_fail (MM_IS_MODEM_HSO (self)); + g_return_if_fail (callback != NULL); + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + if (username) + mm_callback_info_set_data (info, "username", g_strdup (username), g_free); + if (password) + mm_callback_info_set_data (info, "password", g_strdup (password), g_free); + + _internal_hso_modem_authenticate (self, info); +} + +/*****************************************************************************/ + +static void +enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +} + +static void +parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsm *self = MM_GENERIC_GSM (modem); + + if (error) { + mm_generic_gsm_enable_complete (self, error, info); + return; + } + + /* HSO needs manual PIN checking */ + mm_generic_gsm_check_pin (self, enable_done, info); +} + +static void +enable (MMModem *modem, MMModemFn callback, gpointer user_data) +{ + MMModem *parent_modem_iface; + MMCallbackInfo *info; + + info = mm_callback_info_new (modem, callback, user_data); + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->enable (info->modem, parent_enable_done, info); +} + +static void +parent_disable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + mm_callback_info_schedule (info); +} + +static void +disable_done (MMModem *modem, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModem *parent_modem_iface; + + /* Do the normal disable stuff */ + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, parent_disable_done, info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + + info = mm_callback_info_new (modem, callback, user_data); + + /* Kill any existing connection */ + hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info); +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (modem, callback, user_data); + mm_callback_info_schedule (info); +} + + +static void +free_dns_array (gpointer data) +{ + g_array_free ((GArray *) data, TRUE); +} + +static void +ip4_config_invoke (MMCallbackInfo *info) +{ + MMModemIp4Fn callback = (MMModemIp4Fn) info->callback; + + callback (info->modem, + GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-address")), + (GArray *) mm_callback_info_get_data (info, "ip4-dns"), + info->error, info->user_data); +} + +static void +get_ip4_config_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char **items, **iter; + GArray *dns_array; + int i; + guint32 tmp; + guint cid; + + if (error) { + info->error = g_error_copy (error); + goto out; + } else if (!g_str_has_prefix (response->str, OWANDATA_TAG)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Retrieving failed: invalid response."); + goto out; + } + + cid = hso_get_cid (MM_MODEM_HSO (info->modem)); + dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2); + items = g_strsplit (response->str + strlen (OWANDATA_TAG), ", ", 0); + + for (iter = items, i = 0; *iter; iter++, i++) { + if (i == 0) { /* CID */ + long int num; + + errno = 0; + num = strtol (*iter, NULL, 10); + if (errno != 0 || num < 0 || (guint) num != cid) { + info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Unknown CID in OWANDATA response (" + "got %d, expected %d)", (guint) num, cid); + break; + } + } else if (i == 1) { /* IP address */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + mm_callback_info_set_data (info, "ip4-address", GUINT_TO_POINTER (tmp), NULL); + } else if (i == 3) { /* DNS 1 */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + g_array_append_val (dns_array, tmp); + } else if (i == 4) { /* DNS 2 */ + if (inet_pton (AF_INET, *iter, &tmp) > 0) + g_array_append_val (dns_array, tmp); + } + } + + g_strfreev (items); + mm_callback_info_set_data (info, "ip4-dns", dns_array, free_dns_array); + + out: + mm_callback_info_schedule (info); +} + +static void +get_ip4_config (MMModem *modem, + MMModemIp4Fn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMSerialPort *primary; + + info = mm_callback_info_new_full (modem, ip4_config_invoke, G_CALLBACK (callback), user_data); + command = g_strdup_printf ("AT_OWANDATA=%d", hso_get_cid (MM_MODEM_HSO (modem))); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, command, 3, get_ip4_config_done, info); + g_free (command); +} + +static void +disconnect (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (modem, callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "AT_OWANCALL=1,0,0", 3, NULL, info); +} + +/*****************************************************************************/ + +static void +impl_hso_auth_done (MMModem *modem, + GError *error, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context); +} + +static void +impl_hso_authenticate (MMModemHso *self, + const char *username, + const char *password, + DBusGMethodInvocation *context) +{ + /* DBus doesn't support NULLs */ + if (username && strlen (username) == 0) + username = NULL; + if (password && strlen (password) == 0) + password = NULL; + + mm_hso_modem_authenticate (self, username, password, impl_hso_auth_done, context); +} + +static void +connection_enabled (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MMModemHso *self = MM_MODEM_HSO (user_data); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + char *str; + + str = g_match_info_fetch (info, 2); + if (str[0] == '1') + connect_pending_done (self); + else if (str[0] == '3') { + MMCallbackInfo *cb_info = priv->connect_pending_data; + + if (cb_info) + cb_info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Call setup failed"); + + connect_pending_done (self); + } else if (str[0] == '0') + /* FIXME: disconnected. do something when we have modem status signals */ + ; + + g_free (str); +} + +/*****************************************************************************/ +/* MMModemSimple interface */ + +typedef enum { + SIMPLE_STATE_BEGIN = 0, + SIMPLE_STATE_PARENT_CONNECT, + SIMPLE_STATE_AUTHENTICATE, + SIMPLE_STATE_DONE +} SimpleState; + +static const char * +simple_get_string_property (MMCallbackInfo *info, const char *name, GError **error) +{ + GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties"); + GValue *value; + + value = (GValue *) g_hash_table_lookup (properties, name); + if (!value) + return NULL; + + if (G_VALUE_HOLDS_STRING (value)) + return g_value_get_string (value); + + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Invalid property type for '%s': %s (string expected)", + name, G_VALUE_TYPE_NAME (value)); + + return NULL; +} + +static void +simple_state_machine (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSimple *parent_iface; + const char *username; + const char *password; + GHashTable *properties = (GHashTable *) mm_callback_info_get_data (info, "simple-connect-properties"); + SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state")); + + if (error) { + info->error = g_error_copy (error); + goto out; + } + + switch (state) { + case SIMPLE_STATE_BEGIN: + state = SIMPLE_STATE_PARENT_CONNECT; + parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (modem)); + parent_iface->connect (MM_MODEM_SIMPLE (modem), properties, simple_state_machine, info); + break; + case SIMPLE_STATE_PARENT_CONNECT: + state = SIMPLE_STATE_AUTHENTICATE; + username = simple_get_string_property (info, "username", &info->error); + password = simple_get_string_property (info, "password", &info->error); + mm_hso_modem_authenticate (MM_MODEM_HSO (modem), username, password, simple_state_machine, info); + break; + case SIMPLE_STATE_AUTHENTICATE: + state = SIMPLE_STATE_DONE; + break; + default: + break; + } + + out: + if (info->error || state == SIMPLE_STATE_DONE) + mm_callback_info_schedule (info); + else + mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL); +} + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (simple), callback, user_data); + mm_callback_info_set_data (info, "simple-connect-properties", + g_hash_table_ref (properties), + (GDestroyNotify) g_hash_table_unref); + + simple_state_machine (MM_MODEM (simple), NULL, info); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + const char *sys[] = { "tty", "net", NULL }; + GUdevClient *client; + GUdevDevice *device = NULL; + MMPort *port = NULL; + const char *sysfs_path; + + client = g_udev_client_new (sys); + if (!client) { + g_set_error (error, 0, 0, "Could not get udev client."); + return FALSE; + } + + device = g_udev_client_query_by_subsystem_and_name (client, subsys, name); + if (!device) { + g_set_error (error, 0, 0, "Could not get udev device."); + goto out; + } + + sysfs_path = g_udev_device_get_sysfs_path (device); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get udev device sysfs path."); + goto out; + } + + if (!strcmp (subsys, "tty")) { + char *hsotype_path; + char *contents = NULL; + + hsotype_path = g_build_filename (sysfs_path, "hsotype", NULL); + if (g_file_get_contents (hsotype_path, &contents, NULL, NULL)) { + if (g_str_has_prefix (contents, "Control")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_str_has_prefix (contents, "Application") || g_str_has_prefix (contents, "Application2")) + ptype = MM_PORT_TYPE_SECONDARY; + g_free (contents); + } + g_free (hsotype_path); + } + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (!port) + goto out; + + if (MM_IS_SERIAL_PORT (port)) { + g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 10000, NULL); + if (ptype == MM_PORT_TYPE_PRIMARY) { + GRegex *regex; + + mm_generic_gsm_set_unsolicited_registration (gsm, TRUE); + + regex = g_regex_new ("_OWANCALL: (\\d),\\s*(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, connection_enabled, modem, NULL); + g_regex_unref (regex); + } + } + +out: + if (device) + g_object_unref (device); + g_object_unref (client); + return !!port; +} + +/*****************************************************************************/ + +static void +mm_modem_hso_init (MMModemHso *self) +{ +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + +static void +modem_init (MMModem *modem_class) +{ + modem_class->enable = enable; + modem_class->disable = disable; + modem_class->connect = do_connect; + modem_class->get_ip4_config = get_ip4_config; + modem_class->disconnect = disconnect; + modem_class->grab_port = grab_port; +} + +static void +finalize (GObject *object) +{ + /* Clear the pending connection if necessary */ + connect_pending_done (MM_MODEM_HSO (object)); + + G_OBJECT_CLASS (mm_modem_hso_parent_class)->finalize (object); +} + +static void +mm_modem_hso_class_init (MMModemHsoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_hso_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemHsoPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; +} + diff --git a/plugins/mm-modem-hso.h b/plugins/mm-modem-hso.h new file mode 100644 index 0000000..e2d7623 --- /dev/null +++ b/plugins/mm-modem-hso.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_HSO_H +#define MM_MODEM_HSO_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_HSO (mm_modem_hso_get_type ()) +#define MM_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HSO, MMModemHso)) +#define MM_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HSO, MMModemHsoClass)) +#define MM_IS_MODEM_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HSO)) +#define MM_IS_MODEM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HSO)) +#define MM_MODEM_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HSO, MMModemHsoClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemHso; + +typedef struct { + MMGenericGsmClass parent; +} MMModemHsoClass; + +GType mm_modem_hso_get_type (void); + +MMModem *mm_modem_hso_new (const char *device, + const char *driver, + const char *plugin); + +void mm_hso_modem_authenticate (MMModemHso *self, + const char *username, + const char *password, + MMModemFn callback, + gpointer user_data); + +#endif /* MM_MODEM_HSO_H */ diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c new file mode 100644 index 0000000..3b63a48 --- /dev/null +++ b/plugins/mm-modem-huawei-cdma.c @@ -0,0 +1,316 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-huawei-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemHuaweiCdma, mm_modem_huawei_cdma, MM_TYPE_GENERIC_CDMA, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_huawei_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI_CDMA, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, + MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + NULL)); +} + +/* Unsolicited message handlers */ + +static gint +parse_quality (const char *str, const char *detail) +{ + long int quality = 0; + + errno = 0; + quality = strtol (str, NULL, 10); + if (errno == 0) { + quality = CLAMP (quality, 0, 100); + g_debug ("%s: %ld", detail, quality); + return (gint) quality; + } + return -1; +} + +static void +handle_1x_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); + char *str; + gint quality; + + str = g_match_info_fetch (match_info, 1); + quality = parse_quality (str, "1X signal quality"); + g_free (str); + + if (quality >= 0) + mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (self), (guint32) quality); +} + +static void +handle_evdo_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); + char *str; + gint quality; + + str = g_match_info_fetch (match_info, 1); + quality = parse_quality (str, "EVDO signal quality"); + g_free (str); + + if (quality >= 0) + mm_generic_cdma_update_evdo_quality (MM_GENERIC_CDMA (self), (guint32) quality); +} + +/*****************************************************************************/ + +static const char * +strip_response (const char *resp, const char *cmd) +{ + const char *p = resp; + + if (p) { + if (!strncmp (p, cmd, strlen (cmd))) + p += strlen (cmd); + while (*p == ' ') + p++; + } + return p; +} + +static gboolean +uint_from_match_item (GMatchInfo *match_info, guint32 num, guint32 *val) +{ + long int tmp; + char *str; + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + g_return_val_if_fail (str != NULL, FALSE); + + errno = 0; + tmp = strtol (str, NULL, 10); + if (errno == 0 && tmp >= 0 && tmp <= G_MAXUINT) { + *val = (guint32) tmp; + success = TRUE; + } + g_free (str); + return success; +} + +static void +sysinfo_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GRegex *r; + GMatchInfo *match_info; + const char *reply; + gboolean success = FALSE; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + return; + } + + reply = strip_response (response->str, "^SYSINFO:"); + + /* Format is "<srv_status>,<srv_domain>,<roam_status>,<sys_mode>,<sim_state>" */ + r = g_regex_new ("\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + if (!r) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse sysinfo results (regex creation failed)."); + goto done; + } + + g_regex_match (r, reply, 0, &match_info); + if (g_match_info_get_match_count (match_info) >= 5) { + MMModemCdmaRegistrationState reg_state; + guint32 val = 0; + + /* At this point the generic code already knows we've been registered */ + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED; + + if (uint_from_match_item (match_info, 1, &val)) { + if (val == 2) { + /* Service available, check roaming state */ + val = 0; + if (uint_from_match_item (match_info, 3, &val)) { + if (val == 0) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME; + else if (val == 1) + reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING; + } + } + } + + /* Check service type */ + val = 0; + if (uint_from_match_item (match_info, 4, &val)) { + if (val == 2) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); + else if (val == 4) + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); + else if (val == 8) { + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); + } + } else { + /* Say we're registered to something even though sysmode parsing failed */ + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); + } + success = TRUE; + } + +done: + g_match_info_free (match_info); + g_regex_unref (r); + + if (!success && !info->error) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse sysinfo results."); + } + + mm_callback_info_schedule (info); +} + +static void +query_registration_state (MMGenericCdma *cdma, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary, *secondary, *port; + + port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY); + + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data); + + if (mm_port_get_connected (MM_PORT (primary))) { + if (!secondary) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot get query registration state while connected"); + mm_callback_info_schedule (info); + return; + } + + /* Use secondary port if primary is connected */ + port = secondary; + } + + mm_serial_port_queue_command (port, "^SYSINFO", 3, sysinfo_done, info); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMPort *port = NULL; + GRegex *regex; + + port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error); + if (port && MM_IS_SERIAL_PORT (port)) { + gboolean evdo0 = FALSE, evdoA = FALSE; + + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + + /* 1x signal level */ + regex = g_regex_new ("\\r\\n\\^RSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_1x_quality_change, modem, NULL); + g_regex_unref (regex); + + g_object_get (G_OBJECT (modem), + MM_GENERIC_CDMA_EVDO_REV0, &evdo0, + MM_GENERIC_CDMA_EVDO_REVA, &evdoA, + NULL); + + if (evdo0 || evdoA) { + /* EVDO signal level */ + regex = g_regex_new ("\\r\\n\\^HRSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_evdo_quality_change, modem, NULL); + g_regex_unref (regex); + } + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_huawei_cdma_init (MMModemHuaweiCdma *self) +{ +} + +static void +mm_modem_huawei_cdma_class_init (MMModemHuaweiCdmaClass *klass) +{ + MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass); + + mm_modem_huawei_cdma_parent_class = g_type_class_peek_parent (klass); + + cdma_class->query_registration_state = query_registration_state; +} + diff --git a/plugins/mm-modem-huawei-cdma.h b/plugins/mm-modem-huawei-cdma.h new file mode 100644 index 0000000..738f2d9 --- /dev/null +++ b/plugins/mm-modem-huawei-cdma.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_HUAWEI_CDMA_H +#define MM_MODEM_HUAWEI_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_HUAWEI_CDMA (mm_modem_huawei_cdma_get_type ()) +#define MM_MODEM_HUAWEI_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdma)) +#define MM_MODEM_HUAWEI_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdmaClass)) +#define MM_IS_MODEM_HUAWEI_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI_CDMA)) +#define MM_IS_MODEM_HUAWEI_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI_CDMA)) +#define MM_MODEM_HUAWEI_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI_CDMA, MMModemHuaweiCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemHuaweiCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemHuaweiCdmaClass; + +GType mm_modem_huawei_cdma_get_type (void); + +MMModem *mm_modem_huawei_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_HUAWEI_CDMA_H */ diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c new file mode 100644 index 0000000..d450f25 --- /dev/null +++ b/plugins/mm-modem-huawei-gsm.c @@ -0,0 +1,621 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-huawei-gsm.h" +#include "mm-modem-gsm-network.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); + +G_DEFINE_TYPE_EXTENDED (MMModemHuaweiGsm, mm_modem_huawei_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)) + + +#define MM_MODEM_HUAWEI_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmPrivate)) + +typedef struct { + /* Cached state */ + guint signal_quality; + MMModemGsmMode mode; + MMModemGsmBand band; +} MMModemHuaweiGsmPrivate; + +MMModem * +mm_modem_huawei_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_HUAWEI_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +static gboolean +parse_syscfg (MMModemHuaweiGsm *self, + const char *reply, + int *mode_a, + int *mode_b, + guint32 *band, + int *unknown1, + int *unknown2) +{ + if (reply == NULL || strncmp (reply, "^SYSCFG:", 8)) + return FALSE; + + if (sscanf (reply + 8, "%d,%d,%x,%d,%d", mode_a, mode_b, band, unknown1, unknown2)) { + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + + /* Network mode */ + if (*mode_a == 2 && *mode_b == 1) + priv->mode = MM_MODEM_GSM_MODE_2G_PREFERRED; + else if (*mode_a == 2 && *mode_b == 2) + priv->mode = MM_MODEM_GSM_MODE_3G_PREFERRED; + else if (*mode_a == 13 && *mode_b == 1) + priv->mode = MM_MODEM_GSM_MODE_2G_ONLY; + else if (*mode_a == 14 && *mode_b == 2) + priv->mode = MM_MODEM_GSM_MODE_3G_ONLY; + + /* Band */ + if (*band == 0x3FFFFFFF) + priv->band = MM_MODEM_GSM_BAND_ANY; + else if (*band == 0x400380) + priv->band = MM_MODEM_GSM_BAND_DCS; + else if (*band == 0x200000) + priv->band = MM_MODEM_GSM_BAND_PCS; + + return TRUE; + } + + return FALSE; +} + +static void +set_network_mode_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + + if (error) + info->error = g_error_copy (error); + else + /* Success, cache the value */ + priv->mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode")); + + mm_callback_info_schedule (info); +} + +static void +set_network_mode_get_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } else { + int a, b, u1, u2; + guint32 band; + + if (parse_syscfg (MM_MODEM_HUAWEI_GSM (info->modem), response->str, &a, &b, &band, &u1, &u2)) { + char *command; + + switch (GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"))) { + case MM_MODEM_GSM_MODE_ANY: + a = 2; + b = 0; + break; + case MM_MODEM_GSM_MODE_GPRS: + case MM_MODEM_GSM_MODE_EDGE: + case MM_MODEM_GSM_MODE_2G_ONLY: + a = 13; + b = 1; + break; + case MM_MODEM_GSM_MODE_UMTS: + case MM_MODEM_GSM_MODE_HSDPA: + case MM_MODEM_GSM_MODE_HSUPA: + case MM_MODEM_GSM_MODE_HSPA: + case MM_MODEM_GSM_MODE_3G_ONLY: + a = 14; + b = 2; + break; + case MM_MODEM_GSM_MODE_2G_PREFERRED: + a = 2; + b = 1; + break; + case MM_MODEM_GSM_MODE_3G_PREFERRED: + a = 2; + b = 2; + break; + default: + break; + } + + command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2); + mm_serial_port_queue_command (port, command, 3, set_network_mode_done, info); + g_free (command); + } + } +} + +static void +set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + switch (mode) { + case MM_MODEM_GSM_MODE_ANY: + case MM_MODEM_GSM_MODE_GPRS: + case MM_MODEM_GSM_MODE_EDGE: + case MM_MODEM_GSM_MODE_UMTS: + case MM_MODEM_GSM_MODE_HSDPA: + case MM_MODEM_GSM_MODE_HSUPA: + case MM_MODEM_GSM_MODE_HSPA: + case MM_MODEM_GSM_MODE_2G_PREFERRED: + case MM_MODEM_GSM_MODE_3G_PREFERRED: + case MM_MODEM_GSM_MODE_2G_ONLY: + case MM_MODEM_GSM_MODE_3G_ONLY: + /* Allowed values */ + mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, set_network_mode_get_done, info); + return; + default: + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode."); + break; + } + + mm_callback_info_schedule (info); +} + +static void +get_network_mode_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + int mode_a, mode_b, u1, u2; + guint32 band; + + if (error) + info->error = g_error_copy (error); + else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2)) + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL); + + mm_callback_info_schedule (info); +} + +static void +get_network_mode (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + + if (priv->mode != MM_MODEM_GSM_MODE_ANY) { + /* have cached mode (from an unsolicited message). Use that */ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->mode), NULL); + mm_callback_info_schedule (info); + } else { + /* Get it from modem */ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, get_network_mode_done, info); + } +} + +static void +set_band_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + + if (error) + info->error = g_error_copy (error); + else + /* Success, cache the value */ + priv->band = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band")); + + mm_callback_info_schedule (info); +} + +static void +set_band_get_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } else { + int a, b, u1, u2; + guint32 band; + + if (parse_syscfg (self, response->str, &a, &b, &band, &u1, &u2)) { + char *command; + + switch (GPOINTER_TO_UINT (mm_callback_info_get_data (info, "band"))) { + case MM_MODEM_GSM_BAND_ANY: + band = 0x3FFFFFFF; + break; + case MM_MODEM_GSM_BAND_EGSM: + band = 0x100; + break; + case MM_MODEM_GSM_BAND_DCS: + band = 0x80; + break; + case MM_MODEM_GSM_BAND_U2100: + band = 0x400000; + break; + case MM_MODEM_GSM_BAND_PCS: + band = 0x200000; + break; + case MM_MODEM_GSM_BAND_G850: + band = 0x80000; + break; + default: + break; + } + + command = g_strdup_printf ("AT^SYSCFG=%d,%d,%X,%d,%d", a, b, band, u1, u2); + mm_serial_port_queue_command (port, command, 3, set_band_done, info); + g_free (command); + } + } +} + +static void +set_band (MMModemGsmNetwork *modem, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + switch (band) { + case MM_MODEM_GSM_BAND_ANY: + case MM_MODEM_GSM_BAND_EGSM: + case MM_MODEM_GSM_BAND_DCS: + case MM_MODEM_GSM_BAND_U2100: + case MM_MODEM_GSM_BAND_PCS: + mm_callback_info_set_data (info, "band", GUINT_TO_POINTER (band), NULL); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, set_band_get_done, info); + return; + default: + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band."); + break; + } + + mm_callback_info_schedule (info); +} + +static void +get_band_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (info->modem); + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + int mode_a, mode_b, u1, u2; + guint32 band; + + if (error) + info->error = g_error_copy (error); + else if (parse_syscfg (self, response->str, &mode_a, &mode_b, &band, &u1, &u2)) + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL); + + mm_callback_info_schedule (info); +} + +static void +get_band (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + MMSerialPort *primary; + + if (priv->band != MM_MODEM_GSM_BAND_ANY) { + /* have cached mode (from an unsolicited message). Use that */ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->band), NULL); + mm_callback_info_schedule (info); + } else { + /* Get it from modem */ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "AT^SYSCFG?", 3, get_band_done, info); + } +} + +static void +get_signal_quality (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (modem); + + if (priv->signal_quality) { + /* have cached signal quality (from an unsolicited message). Use that */ + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->signal_quality), NULL); + mm_callback_info_schedule (info); + } else { + /* Use the generic implementation */ + MMModemGsmNetwork *parent_gsm_network_iface; + + parent_gsm_network_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (modem)); + parent_gsm_network_iface->get_signal_quality (modem, callback, user_data); + } +} + +/* Unsolicited message handlers */ + +static void +handle_signal_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data); + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + char *str; + int quality; + + str = g_match_info_fetch (match_info, 1); + quality = atoi (str); + g_free (str); + + if (quality == 99) + /* 99 means unknown */ + quality = 0; + else + /* Normalize the quality */ + quality = quality * 100 / 31; + + g_debug ("Signal quality: %d", quality); + priv->signal_quality = (guint32) quality; + mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (self), (guint32) quality); +} + +static void +handle_mode_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiGsm *self = MM_MODEM_HUAWEI_GSM (user_data); + MMModemHuaweiGsmPrivate *priv = MM_MODEM_HUAWEI_GSM_GET_PRIVATE (self); + char *str; + int a; + int b; + + str = g_match_info_fetch (match_info, 1); + a = atoi (str); + g_free (str); + + str = g_match_info_fetch (match_info, 2); + b = atoi (str); + g_free (str); + + if (a == 3 && b == 2) + priv->mode = MM_MODEM_GSM_MODE_GPRS; + else if (a == 3 && b == 3) + priv->mode = MM_MODEM_GSM_MODE_EDGE; + else if (a == 5 && b == 4) + priv->mode = MM_MODEM_GSM_MODE_UMTS; + else if (a == 5 && b == 5) + priv->mode = MM_MODEM_GSM_MODE_HSDPA; + else if (a == 5 && b == 6) + priv->mode = MM_MODEM_GSM_MODE_HSUPA; + else if (a == 5 && b == 7) + priv->mode = MM_MODEM_GSM_MODE_HSPA; + else { + g_warning ("Couldn't parse mode change value: '%s'", str); + return; + } + + g_debug ("Mode: %d", priv->mode); + mm_modem_gsm_network_mode (MM_MODEM_GSM_NETWORK (self), priv->mode); +} + +static void +handle_status_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + char *str; + int n1, n2, n3, n4, n5, n6, n7; + + str = g_match_info_fetch (match_info, 1); + if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7)) { + g_debug ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n", + n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024); + } + g_free (str); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + const char *sys[] = { "tty", NULL }; + GUdevClient *client; + GUdevDevice *device = NULL; + MMPort *port = NULL; + int usbif; + + client = g_udev_client_new (sys); + if (!client) { + g_set_error (error, 0, 0, "Could not get udev client."); + return FALSE; + } + + device = g_udev_client_query_by_subsystem_and_name (client, subsys, name); + if (!device) { + g_set_error (error, 0, 0, "Could not get udev device."); + goto out; + } + + usbif = g_udev_device_get_property_as_int (device, "ID_USB_INTERFACE_NUM"); + if (usbif < 0) { + g_set_error (error, 0, 0, "Could not get USB device interface number."); + goto out; + } + + if (usbif == 0) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + } else if (suggested_type == MM_PORT_TYPE_SECONDARY) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + + if (port && MM_IS_SERIAL_PORT (port)) { + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + if (ptype == MM_PORT_TYPE_SECONDARY) { + GRegex *regex; + + mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE); + + regex = g_regex_new ("\\r\\n\\^RSSI:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_signal_quality_change, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_mode_change, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_status_change, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL); + g_regex_unref (regex); + } + } + +out: + if (device) + g_object_unref (device); + g_object_unref (client); + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->set_network_mode = set_network_mode; + class->get_network_mode = get_network_mode; + class->set_band = set_band; + class->get_band = get_band; + class->get_signal_quality = get_signal_quality; +} + +static void +mm_modem_huawei_gsm_init (MMModemHuaweiGsm *self) +{ +} + +static void +mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_huawei_gsm_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemHuaweiGsmPrivate)); +} + diff --git a/plugins/mm-modem-huawei-gsm.h b/plugins/mm-modem-huawei-gsm.h new file mode 100644 index 0000000..9c2ec34 --- /dev/null +++ b/plugins/mm-modem-huawei-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_HUAWEI_GSM_H +#define MM_MODEM_HUAWEI_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_HUAWEI_GSM (mm_modem_huawei_gsm_get_type ()) +#define MM_MODEM_HUAWEI_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsm)) +#define MM_MODEM_HUAWEI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmClass)) +#define MM_IS_MODEM_HUAWEI_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_HUAWEI_GSM)) +#define MM_IS_MODEM_HUAWEI_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_HUAWEI_GSM)) +#define MM_MODEM_HUAWEI_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_HUAWEI_GSM, MMModemHuaweiGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemHuaweiGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemHuaweiGsmClass; + +GType mm_modem_huawei_gsm_get_type (void); + +MMModem *mm_modem_huawei_gsm_new (const char *device, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_HUAWEI_GSM_H */ diff --git a/plugins/mm-modem-mbm.c b/plugins/mm-modem-mbm.c new file mode 100644 index 0000000..686b35c --- /dev/null +++ b/plugins/mm-modem-mbm.c @@ -0,0 +1,830 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * Author: Per Hallsmark <per.hallsmark@ericsson.com> + * Bjorn Runaker <bjorn.runaker@ericsson.com> + * Torgny Johansson <torgny.johansson@ericsson.com> + * Jonas Sjöquist <jonas.sjoquist@ericsson.com> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mm-modem-mbm.h" +#include "mm-modem-simple.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); +static void modem_simple_init (MMModemSimple *class); + +G_DEFINE_TYPE_EXTENDED (MMModemMbm, mm_modem_mbm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init)) + +#define MM_MODEM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_MBM, MMModemMbmPrivate)) + +#define MBM_E2NAP_DISCONNECTED 0 +#define MBM_E2NAP_CONNECTED 1 +#define MBM_E2NAP_CONNECTING 2 + +#define MBM_SIGNAL_INDICATOR 2 + +#define MBM_NETWORK_MODE_ANY 1 +#define MBM_NETWORK_MODE_2G 5 +#define MBM_NETWORK_MODE_3G 6 + +#define MBM_ERINFO_2G_GPRS 1 +#define MBM_ERINFO_2G_EGPRS 2 +#define MBM_ERINFO_3G_UMTS 1 +#define MBM_ERINFO_3G_HSDPA 2 + +typedef struct { + guint reg_id; + gboolean have_emrdy; + char *network_device; + MMCallbackInfo *pending_connect_info; + int account_index; + int network_mode; + const char *username; + const char *password; +} MMModemMbmPrivate; + +enum { + PROP_0, + PROP_NETWORK_DEVICE, + + LAST_PROP +}; + +static void +mbm_modem_authenticate (MMModemMbm *self, + const char *username, + const char *password, + gpointer user_data); + +static const char * +mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error); + +static uint +mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error); + +MMModem * +mm_modem_mbm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_MBM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP, + NULL)); +} + +/*****************************************************************************/ +/* Gsm network class override functions */ +/*****************************************************************************/ + +typedef struct { + MMModemGsmNetwork *modem; + const char *network_id; + MMModemFn callback; + gpointer user_data; +} RegisterData; + +static gboolean +register_done (gpointer user_data) +{ + RegisterData *reg_data = user_data; + MMModemMbm *self = MM_MODEM_MBM (reg_data->modem); + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + MMModemGsmNetwork *parent_modem_iface; + MMSerialPort *primary; + + priv->reg_id = 0; + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_serial_port_queue_command (primary, "+CREG=1", 3, NULL, NULL); + mm_serial_port_queue_command (primary, "+CMER=3,0,0,1", 3, NULL, NULL); + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)); + parent_modem_iface->do_register (MM_MODEM_GSM_NETWORK (self), + reg_data->network_id, + reg_data->callback, + reg_data->user_data); + return FALSE; +} + +static void +do_register (MMModemGsmNetwork *modem, + const char *network_id, + MMModemFn callback, + gpointer user_data) +{ + MMModemMbm *self = MM_MODEM_MBM (modem); + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + RegisterData *reg_data; + + reg_data = g_malloc0 (sizeof(RegisterData)); + reg_data->modem = modem; + reg_data->network_id = network_id; + reg_data->callback = callback; + reg_data->user_data = user_data; + + /* wait so sim pin is done */ + if (priv->reg_id) + g_source_remove (priv->reg_id); + priv->reg_id = g_timeout_add_full (G_PRIORITY_DEFAULT, 500, + register_done, reg_data, + g_free); +} + +static int +mbm_parse_network_mode (MMModemGsmMode network_mode) +{ + switch (network_mode) { + case MM_MODEM_GSM_MODE_ANY: + case MM_MODEM_GSM_MODE_3G_PREFERRED: + case MM_MODEM_GSM_MODE_2G_PREFERRED: + return MBM_NETWORK_MODE_ANY; + case MM_MODEM_GSM_MODE_GPRS: + case MM_MODEM_GSM_MODE_EDGE: + case MM_MODEM_GSM_MODE_2G_ONLY: + return MBM_NETWORK_MODE_2G; + case MM_MODEM_GSM_MODE_3G_ONLY: + case MM_MODEM_GSM_MODE_HSDPA: + case MM_MODEM_GSM_MODE_HSUPA: + case MM_MODEM_GSM_MODE_HSPA: + return MBM_NETWORK_MODE_3G; + default: + return MBM_NETWORK_MODE_ANY; + } +} + +static void +mbm_set_network_mode_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + char *command; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + command = g_strdup_printf ("+CFUN=%d", mbm_parse_network_mode (mode)); + mm_serial_port_queue_command (primary, command, 3, mbm_set_network_mode_done, info); + g_free (command); +} + +static void +get_network_mode_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + char *erinfo; + int mode = 0, gsm = 0, umts = 0; + gboolean parsed = FALSE; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + erinfo = strstr (response->str, "*ERINFO:"); + if (!erinfo) + goto done; + + if (sscanf (erinfo + 8, "%d,%d,%d", &mode, &gsm, &umts) != 3) + goto done; + + if (gsm || umts) { + MMModemGsmMode mm_mode = MM_MODEM_GSM_MODE_ANY; + + if (gsm == MBM_ERINFO_2G_GPRS) + mm_mode = MM_MODEM_GSM_MODE_GPRS; + else if (gsm == MBM_ERINFO_2G_EGPRS) + mm_mode = MM_MODEM_GSM_MODE_EDGE; + else if (umts == MBM_ERINFO_3G_UMTS) + mm_mode = MM_MODEM_GSM_MODE_UMTS; + else if (umts == MBM_ERINFO_3G_HSDPA) + mm_mode = MM_MODEM_GSM_MODE_HSDPA; + else + g_debug ("%s unknown network mode %d,%d", __FUNCTION__, gsm, umts); + + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_mode), NULL); + parsed = TRUE; + } + +done: + if (!error && !parsed) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse network mode results"); + } + + mm_callback_info_schedule (info); +} + +static void +get_network_mode (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "*ERINFO?", 3, get_network_mode_done, info); +} + +/*****************************************************************************/ +/* Simple Modem class override functions */ +/*****************************************************************************/ + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (simple); + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSimple *parent_iface; + uint network_mode = 0; + + priv->username = mbm_simple_get_string_property (properties, "username", &info->error); + priv->password = mbm_simple_get_string_property (properties, "password", &info->error); + + network_mode = mbm_simple_get_uint_property (properties, "network_mode", &info->error); + priv->network_mode = mbm_parse_network_mode (network_mode); + + parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); + parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +mbm_enable_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); +} + +static void +mbm_enap0_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); + char *command; + + if (!priv->network_mode) + priv->network_mode = MBM_NETWORK_MODE_ANY; + + command = g_strdup_printf ("+CFUN=%d", priv->network_mode); + mm_serial_port_queue_command (port, command, 3, mbm_enable_done, info); + g_free (command); +} + +static void +mbm_init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + if (!priv->network_mode) + priv->network_mode = MBM_NETWORK_MODE_ANY; + + mm_serial_port_queue_command (port, "*ENAP=0", 3, mbm_enap0_done, info); +} + +static void +do_init (MMSerialPort *port, MMCallbackInfo *info) +{ + mm_serial_port_queue_command (port, "&F E0 V1 X4 &C1 +CMEE=1", 3, mbm_init_done, info); +} + +static void +mbm_emrdy_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (info->modem); + + if ( error + && error->domain == MM_SERIAL_ERROR + && error->code == MM_SERIAL_RESPONSE_TIMEOUT) { + g_warning ("%s: timed out waiting for EMRDY response.", __func__); + } else + priv->have_emrdy = TRUE; + + do_init (port, info); +} + +static void +do_enable (MMGenericGsm *self, MMModemFn callback, gpointer user_data) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + + primary = mm_generic_gsm_get_port (self, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + if (priv->have_emrdy) { + /* Modem is ready, no need to check EMRDY */ + do_init (primary, info); + } else + mm_serial_port_queue_command (primary, "*EMRDY?", 5, mbm_emrdy_done, info); +} + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_creg_cmer_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) + +{ + MMModem *parent_modem_iface; + DisableInfo *info = user_data; + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, info->callback, info->user_data); + g_free (info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMSerialPort *primary; + DisableInfo *info; + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Turn off unsolicited +CIEV signal strength indicator */ + mm_serial_port_queue_command (primary, "+CREG=0;+CMER=0", 5, disable_creg_cmer_done, info); +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (modem); + + mm_modem_set_state (modem, MM_MODEM_STATE_CONNECTING, MM_MODEM_STATE_REASON_NONE); + + info = mm_callback_info_new (modem, callback, user_data); + priv->pending_connect_info = info; + + mbm_modem_authenticate (MM_MODEM_MBM (modem), priv->username, priv->password, info); +} + +static void +disconnect (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + mm_modem_set_state (modem, MM_MODEM_STATE_DISCONNECTING, MM_MODEM_STATE_REASON_NONE); + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "*ENAP=0", 3, NULL, NULL); + + mm_generic_gsm_update_enabled_state (MM_GENERIC_GSM (modem), FALSE, MM_MODEM_STATE_REASON_NONE); + + info = mm_callback_info_new (modem, callback, user_data); + mm_callback_info_schedule (info); +} + +/*****************************************************************************/ + +static void +mbm_emrdy_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + MM_MODEM_MBM_GET_PRIVATE (user_data)->have_emrdy = TRUE; +} + +static void +mbm_pacsp_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + return; +} + +static void +mbm_ciev_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + int quality = 0, ind = 0; + const char *str; + + str = g_match_info_fetch (info, 1); + if (str) + ind = atoi (str); + + if (ind == MBM_SIGNAL_INDICATOR) { + str = g_match_info_fetch (info, 2); + if (str) { + quality = atoi (str); + mm_modem_gsm_network_signal_quality (MM_MODEM_GSM_NETWORK (user_data), quality * 20); + } + } +} + +static void +mbm_do_connect_done (MMModemMbm *self) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (self); + + if (priv->pending_connect_info) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (self), NULL, priv->pending_connect_info); + priv->pending_connect_info = NULL; + } +} + +static void +mbm_e2nap_received (MMSerialPort *port, + GMatchInfo *info, + gpointer user_data) +{ + int state = 0; + const char *str; + + str = g_match_info_fetch (info, 1); + if (str) + state = atoi (str); + + if (MBM_E2NAP_DISCONNECTED == state) + g_debug ("%s, disconnected", __func__); + else if (MBM_E2NAP_CONNECTED == state) { + g_debug ("%s, connected", __func__); + mbm_do_connect_done (MM_MODEM_MBM (user_data)); + } else if (MBM_E2NAP_CONNECTING == state) + g_debug("%s, connecting", __func__); + else { + /* Should not happen */ + g_debug("%s, undefined e2nap status",__FUNCTION__); + g_assert_not_reached (); + } +} + +static void +enap_poll_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + guint state; + guint count; + + g_assert (info); + + count = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mbm-enap-poll-count")); + + if (sscanf (response->str, "*ENAP: %d", &state) == 1 && state == 1) { + /* Success! Connected... */ + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), NULL, info); + return; + } + + mm_callback_info_set_data (info, "mbm-enap-poll-count", GUINT_TO_POINTER (++count), NULL); + + /* lets give it about 50 seconds */ + if (count > 50) { + GError *poll_error; + + poll_error = mm_modem_connect_error_for_code (MM_MODEM_CONNECT_ERROR_BUSY); + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), poll_error, info); + g_error_free (poll_error); + } +} + +static gboolean +enap_poll (gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMSerialPort *port = mm_generic_gsm_get_port (MM_GENERIC_GSM (info->modem), MM_PORT_TYPE_PRIMARY); + + g_assert (port); + + mm_serial_port_queue_command (port, "AT*ENAP?", 3, enap_poll_response, user_data); + /* we cancel this in the _done function if all is fine */ + return TRUE; +} + +static void +enap_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + guint tid; + + if (error) { + mm_generic_gsm_connect_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + tid = g_timeout_add_seconds (1, enap_poll, user_data); + /* remember poll id as callback info object, with source_remove as free func */ + mm_callback_info_set_data (info, "mbm-enap-poll-id", GUINT_TO_POINTER (tid), (GFreeFunc) g_source_remove); + mm_serial_port_queue_command (port, "AT*E2NAP=1", 3, NULL, NULL); +} + +static void +mbm_auth_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsm *modem = MM_GENERIC_GSM (info->modem); + char *command; + guint32 cid; + + if (error) { + mm_generic_gsm_connect_complete (modem, error, info); + return; + } + + cid = mm_generic_gsm_get_cid (modem); + command = g_strdup_printf ("AT*ENAP=1,%d", cid); + mm_serial_port_queue_command (port, command, 3, enap_done, user_data); + g_free (command); +} + +static void +mbm_modem_authenticate (MMModemMbm *self, + const char *username, + const char *password, + gpointer user_data) +{ + MMSerialPort *primary; + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + if (username || password) { + char *command; + + command = g_strdup_printf ("*EIAAUW=%d,1,\"%s\",\"%s\"", + mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)), + username ? username : "", + password ? password : ""); + + mm_serial_port_queue_command (primary, command, 3, mbm_auth_done, user_data); + g_free (command); + } else + mbm_auth_done (primary, NULL, NULL, user_data); +} + +static const char * +mbm_simple_get_string_property (GHashTable *properties, const char *name, GError **error) +{ + GValue *value; + + value = (GValue *) g_hash_table_lookup (properties, name); + if (!value) + return NULL; + + if (G_VALUE_HOLDS_STRING (value)) + return g_value_get_string (value); + + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Invalid property type for '%s': %s (string expected)", + name, G_VALUE_TYPE_NAME (value)); + + return NULL; +} + +static uint +mbm_simple_get_uint_property (GHashTable *properties, const char *name, GError **error) +{ + GValue *value; + + value = (GValue *) g_hash_table_lookup (properties, name); + if (!value) + return 0; + + if (G_VALUE_HOLDS_UINT (value)) + return g_value_get_uint (value); + + g_set_error (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Invalid property type for '%s': %s (uint expected)", + name, G_VALUE_TYPE_NAME (value)); + + return 0; +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (!strcmp (subsys, "tty")) { + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + } + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + GRegex *regex; + + mm_generic_gsm_set_unsolicited_registration (MM_GENERIC_GSM (modem), TRUE); + + regex = g_regex_new ("\\r\\n\\*EMRDY: \\d\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_emrdy_received, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\*E2NAP: (\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_e2nap_received, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_pacsp_received, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\+CIEV: (\\d),(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, mbm_ciev_received, modem, NULL); + g_regex_unref (regex); + + /* also consume unsolicited mbm messages we are not interested in them - see LP: #416418 */ + regex = g_regex_new ("\\R\\*ESTKSMENU:.*\\R", G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, G_REGEX_MATCH_NEWLINE_CRLF, NULL);
+ mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, modem, NULL); + g_regex_unref (regex); + + regex = g_regex_new ("\\r\\n\\*EMWI: (\\d),(\\d).*\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->do_register = do_register; + class->get_network_mode = get_network_mode; + class->set_network_mode = set_network_mode; +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; + modem_class->disable = disable; + modem_class->connect = do_connect; + modem_class->disconnect = disconnect; +} + +static void +mm_modem_mbm_init (MMModemMbm *self) +{ +} + +static void +finalize (GObject *object) +{ + MMModemMbmPrivate *priv = MM_MODEM_MBM_GET_PRIVATE (object); + + if (priv->reg_id) + g_source_remove (priv->reg_id); + + g_free (priv->network_device); + + G_OBJECT_CLASS (mm_modem_mbm_parent_class)->finalize (object); +} + +static void +mm_modem_mbm_class_init (MMModemMbmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_mbm_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemMbmPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + gsm_class->do_enable = do_enable; +} + diff --git a/plugins/mm-modem-mbm.h b/plugins/mm-modem-mbm.h new file mode 100644 index 0000000..8756e47 --- /dev/null +++ b/plugins/mm-modem-mbm.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * Author: Per Hallsmark <per.hallsmark@ericsson.com> + * Bjorn Runaker <bjorn.runaker@ericsson.com> + * Torgny Johansson <torgny.johansson@ericsson.com> + * Jonas Sjöquist <jonas.sjoquist@ericsson.com> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef MM_MODEM_MBM_H +#define MM_MODEM_MBM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_MBM (mm_modem_mbm_get_type ()) +#define MM_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_MBM, MMModemMbm)) +#define MM_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_MBM, MMModemMbmClass)) +#define MM_IS_MODEM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_MBM)) +#define MM_IS_MODEM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_MBM)) +#define MM_MODEM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_MBM, MMModemMbmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemMbm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemMbmClass; + +GType mm_modem_mbm_get_type (void); + +MMModem *mm_modem_mbm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_MBM_H */ diff --git a/plugins/mm-modem-moto-c-gsm.c b/plugins/mm-modem-moto-c-gsm.c new file mode 100644 index 0000000..5910ff2 --- /dev/null +++ b/plugins/mm-modem-moto-c-gsm.c @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mm-modem-moto-c-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm-card.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class); + +G_DEFINE_TYPE_EXTENDED (MMModemMotoCGsm, mm_modem_moto_c_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) + + +MMModem * +mm_modem_moto_c_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_MOTO_C_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ +} + +/*****************************************************************************/ + +static void +get_imei (MMModemGsmCard *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Operation not supported"); + mm_callback_info_schedule (info); +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_imei = get_imei; +} + +/*****************************************************************************/ + +static void +mm_modem_moto_c_gsm_init (MMModemMotoCGsm *self) +{ +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + + /* These devices just don't implement AT+CFUN */ + + switch (prop_id) { + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: + g_value_set_string (value, ""); + break; + default: + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ +} + +static void +mm_modem_moto_c_gsm_class_init (MMModemMotoCGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_moto_c_gsm_parent_class = g_type_class_peek_parent (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_POWER_UP_CMD); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, + MM_GENERIC_GSM_POWER_DOWN_CMD); +} + diff --git a/plugins/mm-modem-moto-c-gsm.h b/plugins/mm-modem-moto-c-gsm.h new file mode 100644 index 0000000..eb1dad1 --- /dev/null +++ b/plugins/mm-modem-moto-c-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_MOTO_C_GSM_H +#define MM_MODEM_MOTO_C_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_MOTO_C_GSM (mm_modem_moto_c_gsm_get_type ()) +#define MM_MODEM_MOTO_C_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_MOTO_C_GSM, MMModemMotoCGsm)) +#define MM_MODEM_MOTO_C_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_MOTO_C_GSM, MMModemMotoCGsmClass)) +#define MM_IS_MODEM_MOTO_C_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_MOTO_C_GSM)) +#define MM_IS_MODEM_MOTO_C_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_MOTO_C_GSM)) +#define MM_MODEM_MOTO_C_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_MOTO_C_GSM, MMModemMotoCGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemMotoCGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemMotoCGsmClass; + +GType mm_modem_moto_c_gsm_get_type (void); + +MMModem *mm_modem_moto_c_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_MOTO_C_GSM_H */ diff --git a/plugins/mm-modem-nokia.c b/plugins/mm-modem-nokia.c new file mode 100644 index 0000000..677a089 --- /dev/null +++ b/plugins/mm-modem-nokia.c @@ -0,0 +1,142 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-nokia.h" +#include "mm-serial-parsers.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemNokia, mm_modem_nokia, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_nokia_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_NOKIA, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_SERIAL_PORT (port)) { + mm_serial_port_set_response_parser (MM_SERIAL_PORT (port), + mm_serial_parser_v1_e1_parse, + mm_serial_parser_v1_e1_new (), + mm_serial_parser_v1_e1_destroy); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_nokia_init (MMModemNokia *self) +{ +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + /* gobject does not like to just have get_property and seems to + * to not honour our overriden properties ... keep this as an empty + * func around */ +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + /* Nokia headsets (at least N85) do not support "power on"; they do + * support "power off" but you proabably do not want to turn off the + * power on your telephone if something went wrong with connecting + * process. So, disabling both these operations. The Nokia GSM/UMTS command + * reference v1.2 also states that only CFUN=0 (turn off but still charge) + * and CFUN=1 (full functionality) are supported, and since the phone has + * to be in CFUN=1 before we'll be able to talk to it in the first place, + * we shouldn't bother with CFUN at all. + */ + switch (prop_id) { + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: + g_value_set_string (value, ""); + break; + default: + break; + } +} + +static void +mm_modem_nokia_class_init (MMModemNokiaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mm_modem_nokia_parent_class = g_type_class_peek_parent (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_POWER_UP_CMD); + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, + MM_GENERIC_GSM_POWER_DOWN_CMD); +} + diff --git a/plugins/mm-modem-nokia.h b/plugins/mm-modem-nokia.h new file mode 100644 index 0000000..0e24619 --- /dev/null +++ b/plugins/mm-modem-nokia.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_NOKIA_H +#define MM_MODEM_NOKIA_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_NOKIA (mm_modem_nokia_get_type ()) +#define MM_MODEM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOKIA, MMModemNokia)) +#define MM_MODEM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOKIA, MMModemNokiaClass)) +#define MM_IS_MODEM_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOKIA)) +#define MM_IS_MODEM_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOKIA)) +#define MM_MODEM_NOKIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOKIA, MMModemNokiaClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemNokia; + +typedef struct { + MMGenericGsmClass parent; +} MMModemNokiaClass; + +GType mm_modem_nokia_get_type (void); + +MMModem *mm_modem_nokia_new (const char *data, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_NOKIA_H */ diff --git a/plugins/mm-modem-novatel-gsm.c b/plugins/mm-modem-novatel-gsm.c new file mode 100644 index 0000000..8189627 --- /dev/null +++ b/plugins/mm-modem-novatel-gsm.c @@ -0,0 +1,184 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-novatel-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemNovatelGsm, mm_modem_novatel_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_novatel_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_NOVATEL_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +init_modem_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); +} + +static void +pin_check_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMGenericGsm *self = MM_GENERIC_GSM (modem); + MMSerialPort *primary; + + if (error) { + mm_generic_gsm_enable_complete (self, error, info); + return; + } + + /* Finish the initialization */ + primary = mm_generic_gsm_get_port (self, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1", 10, init_modem_done, info); +} + +static void +pre_init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + /* Now check the PIN explicitly, novatel doesn't seem to report + * that it needs it otherwise. + */ + mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); +} + +static void +enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = user_data; + + if (error) + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + else + mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data); +} + +static void +do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + primary = mm_generic_gsm_get_port (modem, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_flash (primary, 100, enable_flash_done, info); +} + +static void +dmat_callback (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + mm_serial_port_close (port); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_SERIAL_PORT (port) && (ptype == MM_PORT_TYPE_PRIMARY)) { + /* Flip secondary ports to AT mode */ + if (mm_serial_port_open (MM_SERIAL_PORT (port), NULL)) + mm_serial_port_queue_command (MM_SERIAL_PORT (port), "$NWDMAT=1", 2, dmat_callback, NULL); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +mm_modem_novatel_gsm_init (MMModemNovatelGsm *self) +{ +} + +static void +mm_modem_novatel_gsm_class_init (MMModemNovatelGsmClass *klass) +{ + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_novatel_gsm_parent_class = g_type_class_peek_parent (klass); + + gsm_class->do_enable = do_enable; +} + diff --git a/plugins/mm-modem-novatel-gsm.h b/plugins/mm-modem-novatel-gsm.h new file mode 100644 index 0000000..c2e1138 --- /dev/null +++ b/plugins/mm-modem-novatel-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_NOVATEL_GSM_H +#define MM_MODEM_NOVATEL_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_NOVATEL_GSM (mm_modem_novatel_gsm_get_type ()) +#define MM_MODEM_NOVATEL_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_NOVATEL_GSM, MMModemNovatelGsm)) +#define MM_MODEM_NOVATEL_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_NOVATEL_GSM, MMModemNovatelGsmClass)) +#define MM_IS_MODEM_NOVATEL_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_NOVATEL_GSM)) +#define MM_IS_MODEM_NOVATEL_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_NOVATEL_GSM)) +#define MM_MODEM_NOVATEL_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_NOVATEL_GSM, MMModemNovatelGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemNovatelGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemNovatelGsmClass; + +GType mm_modem_novatel_gsm_get_type (void); + +MMModem *mm_modem_novatel_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_NOVATEL_GSM_H */ diff --git a/plugins/mm-modem-option.c b/plugins/mm-modem-option.c new file mode 100644 index 0000000..2076ae6 --- /dev/null +++ b/plugins/mm-modem-option.c @@ -0,0 +1,244 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-option.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); + +G_DEFINE_TYPE_EXTENDED (MMModemOption, mm_modem_option, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)) + + +MMModem * +mm_modem_option_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_OPTION, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ + +static void +pin_check_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +} + +static gboolean +option_enabled (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + + /* Now check the PIN explicitly, option doesn't seem to report + * that it needs it otherwise. + */ + mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); + return FALSE; +} + +static void +parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + return; + } + + /* Option returns OK on +CFUN=1 right away but needs some time + * to finish initialization + */ + g_timeout_add_seconds (10, option_enabled, info); +} + +static void +enable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMModem *parent_modem_iface; + MMCallbackInfo *info; + + info = mm_callback_info_new (modem, callback, user_data); + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_modem_iface->enable (modem, parent_enable_done, info); +} + +static void +get_network_mode_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + gboolean parsed = FALSE; + + if (error) + info->error = g_error_copy (error); + else if (!g_str_has_prefix (response->str, "_OPSYS: ")) { + int a, b; + + if (sscanf (response->str + 8, "%d,%d", &a, &b)) { + MMModemGsmMode mode = MM_MODEM_GSM_MODE_ANY; + + switch (a) { + case 0: + mode = MM_MODEM_GSM_MODE_2G_ONLY; + break; + case 1: + mode = MM_MODEM_GSM_MODE_3G_ONLY; + break; + case 2: + mode = MM_MODEM_GSM_MODE_2G_PREFERRED; + break; + case 3: + mode = MM_MODEM_GSM_MODE_3G_PREFERRED; + break; + default: + break; + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (mode), NULL); + parsed = TRUE; + } + } + + if (!error && !parsed) + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse network mode results"); + + mm_callback_info_schedule (info); +} + +static void +get_network_mode (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "AT_OPSYS?", 3, get_network_mode_done, info); +} + +static void +set_network_mode_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_network_mode (MMModemGsmNetwork *modem, + MMModemGsmMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + char *command; + int i; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + switch (mode) { + case MM_MODEM_GSM_MODE_ANY: + case MM_MODEM_GSM_MODE_GPRS: + case MM_MODEM_GSM_MODE_EDGE: + case MM_MODEM_GSM_MODE_2G_ONLY: + i = 0; + break; + case MM_MODEM_GSM_MODE_UMTS: + case MM_MODEM_GSM_MODE_HSDPA: + case MM_MODEM_GSM_MODE_HSUPA: + case MM_MODEM_GSM_MODE_HSPA: + case MM_MODEM_GSM_MODE_3G_ONLY: + i = 1; + break; + case MM_MODEM_GSM_MODE_2G_PREFERRED: + i = 2; + break; + case MM_MODEM_GSM_MODE_3G_PREFERRED: + i = 3; + break; + default: + i = 5; + break; + } + + command = g_strdup_printf ("AT_OPSYS=%d,2", i); + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, command, 3, set_network_mode_done, info); + g_free (command); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->enable = enable; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->set_network_mode = set_network_mode; + class->get_network_mode = get_network_mode; +} + +static void +mm_modem_option_init (MMModemOption *self) +{ +} + +static void +mm_modem_option_class_init (MMModemOptionClass *klass) +{ + mm_modem_option_parent_class = g_type_class_peek_parent (klass); +} + diff --git a/plugins/mm-modem-option.h b/plugins/mm-modem-option.h new file mode 100644 index 0000000..4e88607 --- /dev/null +++ b/plugins/mm-modem-option.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_OPTION_H +#define MM_MODEM_OPTION_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_OPTION (mm_modem_option_get_type ()) +#define MM_MODEM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_OPTION, MMModemOption)) +#define MM_MODEM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_OPTION, MMModemOptionClass)) +#define MM_IS_MODEM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_OPTION)) +#define MM_IS_MODEM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_OPTION)) +#define MM_MODEM_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_OPTION, MMModemOptionClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemOption; + +typedef struct { + MMGenericGsmClass parent; +} MMModemOptionClass; + +GType mm_modem_option_get_type (void); + +MMModem *mm_modem_option_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_OPTION_H */ diff --git a/plugins/mm-modem-sierra-cdma.c b/plugins/mm-modem-sierra-cdma.c new file mode 100644 index 0000000..4f3140b --- /dev/null +++ b/plugins/mm-modem-sierra-cdma.c @@ -0,0 +1,383 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-modem-sierra-cdma.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" + +G_DEFINE_TYPE (MMModemSierraCdma, mm_modem_sierra_cdma, MM_TYPE_GENERIC_CDMA) + +#define MM_MODEM_SIERRA_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdmaPrivate)) + +typedef enum { + SYS_MODE_UNKNOWN, + SYS_MODE_NO_SERVICE, + SYS_MODE_CDMA_1X, + SYS_MODE_EVDO_REV0, + SYS_MODE_EVDO_REVA +} SysMode; + +typedef struct { + SysMode sys_mode; +} MMModemSierraCdmaPrivate; + +MMModem * +mm_modem_sierra_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_SIERRA_CDMA, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_GENERIC_CDMA_EVDO_REV0, evdo_rev0, + MM_GENERIC_CDMA_EVDO_REVA, evdo_revA, + NULL)); +} + +/*****************************************************************************/ + +#define MODEM_REG_TAG "Modem has registered" +#define GENERIC_ROAM_TAG "Roaming:" +#define ROAM_1X_TAG "1xRoam:" +#define ROAM_EVDO_TAG "HDRRoam:" +#define SYS_MODE_TAG "Sys Mode:" +#define SYS_MODE_NO_SERVICE_TAG "NO SRV" +#define SYS_MODE_EVDO_TAG "HDR" +#define SYS_MODE_1X_TAG "1x" +#define EVDO_REV_TAG "HDR Revision:" +#define SID_TAG "SID:" + +static gboolean +get_roam_value (const char *reply, const char *tag, gboolean *roaming) +{ + char *p; + + p = strstr (reply, tag); + if (!p) + return FALSE; + + p += strlen (tag); + while (*p && isspace (*p)) + p++; + if (*p == '1') { + *roaming = TRUE; + return TRUE; + } else if (*p == '0') { + *roaming = FALSE; + return TRUE; + } + + return FALSE; +} + +static gboolean +sys_mode_has_service (SysMode mode) +{ + return ( mode == SYS_MODE_CDMA_1X + || mode == SYS_MODE_EVDO_REV0 + || mode == SYS_MODE_EVDO_REVA); +} + +static void +status_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSierraCdmaPrivate *priv = MM_MODEM_SIERRA_CDMA_GET_PRIVATE (info->modem); + char **lines, **iter; + gboolean registered = FALSE; + gboolean have_sid = FALSE; + SysMode evdo_mode = SYS_MODE_UNKNOWN; + SysMode sys_mode = SYS_MODE_UNKNOWN; + gboolean cdma_1x_set = FALSE, evdo_set = FALSE; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + lines = g_strsplit_set (response->str, "\n\r", 0); + if (!lines) { + /* Whatever, just use default registration state */ + goto done; + } + + /* Sierra CDMA parts have two general formats depending on whether they + * support EVDO or not. EVDO parts report both 1x and EVDO roaming status + * while of course 1x parts only report 1x status. Some modems also do not + * report the Roaming information (MP 555 GPS). + * + * AT!STATUS responses: + * + * Unregistered MC5725: + * ----------------------- + * Current band: PCS CDMA + * Current channel: 350 + * SID: 0 NID: 0 1xRoam: 0 HDRRoam: 0 + * Temp: 33 State: 100 Sys Mode: NO SRV + * Pilot NOT acquired + * Modem has NOT registered + * + * Registered MC5725: + * ----------------------- + * Current band: Cellular Sleep + * Current channel: 775 + * SID: 30 NID: 2 1xRoam: 0 HDRRoam: 0 + * Temp: 29 State: 200 Sys Mode: HDR + * Pilot acquired + * Modem has registered + * HDR Revision: A + * + * Unregistered AC580: + * ----------------------- + * Current band: PCS CDMA + * Current channel: 350 + * SID: 0 NID: 0 Roaming: 0 + * Temp: 39 State: 100 Scan Mode: 0 + * Pilot NOT acquired + * Modem has NOT registered + * + * Registered AC580: + * ----------------------- + * Current band: Cellular Sleep + * Current channel: 548 + * SID: 26 NID: 1 Roaming: 1 + * Temp: 39 State: 200 Scan Mode: 0 + * Pilot Acquired + * Modem has registered + */ + + /* We have to handle the two formats slightly differently; for newer formats + * with "Sys Mode", we consider the modem registered if the Sys Mode is not + * "NO SRV". The explicit registration status is just icing on the cake. + * For older formats (no "Sys Mode") we treat the modem as registered if + * the SID is non-zero. + */ + + for (iter = lines; iter && *iter; iter++) { + gboolean bool_val = FALSE; + char *p; + + if (!strncmp (*iter, MODEM_REG_TAG, strlen (MODEM_REG_TAG))) { + registered = TRUE; + continue; + } + + /* Roaming */ + if (get_roam_value (*iter, ROAM_1X_TAG, &bool_val)) { + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, + bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : + MM_MODEM_CDMA_REGISTRATION_STATE_HOME); + cdma_1x_set = TRUE; + } + if (get_roam_value (*iter, ROAM_EVDO_TAG, &bool_val)) { + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, + bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : + MM_MODEM_CDMA_REGISTRATION_STATE_HOME); + evdo_set = TRUE; + } + if (get_roam_value (*iter, GENERIC_ROAM_TAG, &bool_val)) { + MMModemCdmaRegistrationState reg_state; + + reg_state = bool_val ? MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING : + MM_MODEM_CDMA_REGISTRATION_STATE_HOME; + + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, reg_state); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, reg_state); + cdma_1x_set = TRUE; + evdo_set = TRUE; + } + + /* Current system mode */ + p = strstr (*iter, SYS_MODE_TAG); + if (p) { + p += strlen (SYS_MODE_TAG); + while (*p && isspace (*p)) + p++; + if (!strncmp (p, SYS_MODE_NO_SERVICE_TAG, strlen (SYS_MODE_NO_SERVICE_TAG))) + sys_mode = SYS_MODE_NO_SERVICE; + else if (!strncmp (p, SYS_MODE_EVDO_TAG, strlen (SYS_MODE_EVDO_TAG))) + sys_mode = SYS_MODE_EVDO_REV0; + else if (!strncmp (p, SYS_MODE_1X_TAG, strlen (SYS_MODE_1X_TAG))) + sys_mode = SYS_MODE_CDMA_1X; + } + + /* Current EVDO revision if system mode is EVDO */ + p = strstr (*iter, EVDO_REV_TAG); + if (p) { + p += strlen (EVDO_REV_TAG); + while (*p && isspace (*p)) + p++; + if (*p == 'A') + evdo_mode = SYS_MODE_EVDO_REVA; + else if (*p == '0') + evdo_mode = SYS_MODE_EVDO_REV0; + } + + /* SID */ + p = strstr (*iter, SID_TAG); + if (p) { + p += strlen (SID_TAG); + while (*p && isspace (*p)) + p++; + if (isdigit (*p) && (*p != '0')) + have_sid = TRUE; + } + } + + /* Update current system mode */ + if (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA) { + /* Prefer the explicit EVDO mode from EVDO_REV_TAG */ + if (evdo_mode != SYS_MODE_UNKNOWN) + sys_mode = evdo_mode; + } + priv->sys_mode = sys_mode; + + if (registered || have_sid || sys_mode_has_service (sys_mode)) { + /* As a backup, if for some reason the registration states didn't get + * figured out by parsing the status info, set some generic registration + * states here. + */ + if (!cdma_1x_set) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); + + /* Ensure EVDO registration mode is set if we're at least in EVDO mode */ + if (!evdo_set && (sys_mode == SYS_MODE_EVDO_REV0 || sys_mode == SYS_MODE_EVDO_REVA)) + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED); + } else { + /* Not registered */ + mm_generic_cdma_query_reg_state_set_callback_1x_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + mm_generic_cdma_query_reg_state_set_callback_evdo_state (info, MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN); + } + +done: + mm_callback_info_schedule (info); +} + +static void +query_registration_state (MMGenericCdma *cdma, + MMModemCdmaRegistrationStateFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary, *secondary; + MMSerialPort *port; + + port = primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + secondary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_SECONDARY); + + info = mm_generic_cdma_query_reg_state_callback_info_new (cdma, callback, user_data); + + if (mm_port_get_connected (MM_PORT (primary))) { + if (!secondary) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED, + "Cannot get query registration state while connected"); + mm_callback_info_schedule (info); + return; + } + + /* Use secondary port if primary is connected */ + port = secondary; + } + + mm_serial_port_queue_command (port, "!STATUS", 3, status_done, info); +} + +static void +pcstate_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + /* Ignore errors for now; we're not sure if all Sierra CDMA devices support + * at!pcstate. + */ + mm_callback_info_schedule ((MMCallbackInfo *) user_data); +} + +static void +post_enable (MMGenericCdma *cdma, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data); + + primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_serial_port_queue_command (primary, "!pcstate=1", 5, pcstate_done, info); +} + +static void +post_disable (MMGenericCdma *cdma, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (cdma), callback, user_data); + + primary = mm_generic_cdma_get_port (cdma, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_serial_port_queue_command (primary, "!pcstate=0", 5, pcstate_done, info); +} + +/*****************************************************************************/ + +static void +mm_modem_sierra_cdma_init (MMModemSierraCdma *self) +{ +} + +static void +mm_modem_sierra_cdma_class_init (MMModemSierraCdmaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericCdmaClass *cdma_class = MM_GENERIC_CDMA_CLASS (klass); + + mm_modem_sierra_cdma_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemSierraCdmaPrivate)); + + cdma_class->query_registration_state = query_registration_state; + cdma_class->post_enable = post_enable; + cdma_class->post_disable = post_disable; +} + diff --git a/plugins/mm-modem-sierra-cdma.h b/plugins/mm-modem-sierra-cdma.h new file mode 100644 index 0000000..9111b73 --- /dev/null +++ b/plugins/mm-modem-sierra-cdma.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_SIERRA_CDMA_H +#define MM_MODEM_SIERRA_CDMA_H + +#include "mm-generic-cdma.h" + +#define MM_TYPE_MODEM_SIERRA_CDMA (mm_modem_sierra_cdma_get_type ()) +#define MM_MODEM_SIERRA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdma)) +#define MM_MODEM_SIERRA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdmaClass)) +#define MM_IS_MODEM_SIERRA_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIERRA_CDMA)) +#define MM_IS_MODEM_SIERRA_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SIERRA_CDMA)) +#define MM_MODEM_SIERRA_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIERRA_CDMA, MMModemSierraCdmaClass)) + +typedef struct { + MMGenericCdma parent; +} MMModemSierraCdma; + +typedef struct { + MMGenericCdmaClass parent; +} MMModemSierraCdmaClass; + +GType mm_modem_sierra_cdma_get_type (void); + +MMModem *mm_modem_sierra_cdma_new (const char *device, + const char *driver, + const char *plugin, + gboolean evdo_rev0, + gboolean evdo_revA); + +#endif /* MM_MODEM_SIERRA_CDMA_H */ diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c new file mode 100644 index 0000000..ee82234 --- /dev/null +++ b/plugins/mm-modem-sierra-gsm.c @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-sierra-gsm.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemSierraGsm, mm_modem_sierra_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + + +MMModem * +mm_modem_sierra_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_SIERRA_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +pin_check_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); +} + +static gboolean +sierra_enabled (gpointer data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) data; + + /* Now check the PIN explicitly, sierra doesn't seem to report + * that it needs it otherwise. + */ + mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); + return FALSE; +} + +static void +parent_enable_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + return; + } + + /* Sierra returns OK on +CFUN=1 right away but needs some time + * to finish initialization. + */ + g_timeout_add_seconds (10, sierra_enabled, info); +} + +static void +enable (MMModem *modem, MMModemFn callback, gpointer user_data) +{ + MMModem *parent_modem_iface; + MMCallbackInfo *info; + + info = mm_callback_info_new (modem, callback, user_data); + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_modem_iface->enable (modem, parent_enable_done, info); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + + if (port && MM_IS_SERIAL_PORT (port)) + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->enable = enable; + modem_class->grab_port = grab_port; +} + +static void +mm_modem_sierra_gsm_init (MMModemSierraGsm *self) +{ +} + +static void +mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass) +{ + mm_modem_sierra_gsm_parent_class = g_type_class_peek_parent (klass); +} + diff --git a/plugins/mm-modem-sierra-gsm.h b/plugins/mm-modem-sierra-gsm.h new file mode 100644 index 0000000..dd84b30 --- /dev/null +++ b/plugins/mm-modem-sierra-gsm.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_SIERRA_GSM_H +#define MM_MODEM_SIERRA_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_SIERRA_GSM (mm_modem_sierra_gsm_get_type ()) +#define MM_MODEM_SIERRA_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsm)) +#define MM_MODEM_SIERRA_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsmClass)) +#define MM_IS_MODEM_SIERRA_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SIERRA_GSM)) +#define MM_IS_MODEM_SIERRA_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SIERRA_GSM)) +#define MM_MODEM_SIERRA_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SIERRA_GSM, MMModemSierraGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemSierraGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemSierraGsmClass; + +GType mm_modem_sierra_gsm_get_type (void); + +MMModem *mm_modem_sierra_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_SIERRA_GSM_H */ diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c new file mode 100644 index 0000000..92c23ae --- /dev/null +++ b/plugins/mm-modem-zte.c @@ -0,0 +1,235 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "mm-modem-zte.h" +#include "mm-serial-port.h" +#include "mm-errors.h" +#include "mm-callback-info.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +#define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate)) + +typedef struct { + gboolean init_retried; +} MMModemZtePrivate; + +MMModem * +mm_modem_zte_new (const char *device, + const char *driver, + const char *plugin) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_ZTE, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + NULL)); +} + +/*****************************************************************************/ +/* Modem class override functions */ +/*****************************************************************************/ + +static void +init_modem_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); +} + +static void +pin_check_done (MMModem *modem, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMSerialPort *primary; + + if (error) { + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (modem), error, info); + return; + } + + /* Finish the initialization */ + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_serial_port_queue_command (primary, "Z E0 V1 X4 &C1 +CMEE=1;+CFUN=1;", 10, init_modem_done, info); +} + +static void enable_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data); + +static void +pre_init_done (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (info->modem); + + if (error) { + /* Retry the init string one more time; the modem sometimes throws it away */ + if ( !priv->init_retried + && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) { + priv->init_retried = TRUE; + enable_flash_done (port, NULL, user_data); + } else + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + } else { + /* Now check the PIN explicitly, zte doesn't seem to report + that it needs it otherwise */ + mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_check_done, info); + } +} + +static void +enable_flash_done (MMSerialPort *port, GError *error, gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); + else + mm_serial_port_queue_command (port, "E0 V1", 3, pre_init_done, user_data); +} + +static void +do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +{ + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); + MMCallbackInfo *info; + MMSerialPort *primary; + + priv->init_retried = FALSE; + + primary = mm_generic_gsm_get_port (modem, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + mm_serial_port_flash (primary, 100, enable_flash_done, info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); + MMModem *parent_modem_iface; + + priv->init_retried = FALSE; + + /* Do the normal disable stuff */ + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_modem_iface->disable (modem, callback, user_data); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_SERIAL_PORT (port)) { + GRegex *regex; + + mm_generic_gsm_set_unsolicited_registration (gsm, TRUE); + g_object_set (port, MM_PORT_CARRIER_DETECT, FALSE, NULL); + + regex = g_regex_new ("\\r\\n\\+ZUSIMR:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Unsolicted operator display */ + regex = g_regex_new ("\\r\\n\\+ZDONR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* Current network and service domain */ + regex = g_regex_new ("\\r\\n\\+ZPASR: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* SIM request to Build Main Menu */ + regex = g_regex_new ("\\r\\n\\+ZPSTM: (.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + + /* SIM request to Rebuild Main Menu */ + regex = g_regex_new ("\\r\\n\\+ZEND\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, NULL, NULL, NULL); + g_regex_unref (regex); + } + + return !!port; +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->disable = disable; + modem_class->grab_port = grab_port; +} + +static void +mm_modem_zte_init (MMModemZte *self) +{ +} + +static void +mm_modem_zte_class_init (MMModemZteClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_zte_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemZtePrivate)); + + gsm_class->do_enable = do_enable; +} + diff --git a/plugins/mm-modem-zte.h b/plugins/mm-modem-zte.h new file mode 100644 index 0000000..112bae0 --- /dev/null +++ b/plugins/mm-modem-zte.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_MODEM_ZTE_H +#define MM_MODEM_ZTE_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_ZTE (mm_modem_zte_get_type ()) +#define MM_MODEM_ZTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_ZTE, MMModemZte)) +#define MM_MODEM_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_ZTE, MMModemZteClass)) +#define MM_IS_MODEM_ZTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_ZTE)) +#define MM_IS_MODEM_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_ZTE)) +#define MM_MODEM_ZTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_ZTE, MMModemZteClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemZte; + +typedef struct { + MMGenericGsmClass parent; +} MMModemZteClass; + +GType mm_modem_zte_get_type (void); + +MMModem *mm_modem_zte_new (const char *device, + const char *driver, + const char *plugin); + +#endif /* MM_MODEM_ZTE_H */ diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c new file mode 100644 index 0000000..e451714 --- /dev/null +++ b/plugins/mm-plugin-anydata.c @@ -0,0 +1,179 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-anydata.h" +#include "mm-modem-anydata-cdma.h" + +G_DEFINE_TYPE (MMPluginAnydata, mm_plugin_anydata, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_ANYDATA, + MM_PLUGIN_BASE_NAME, "AnyData", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + /* Only CDMA for now */ + if (capabilities & CAP_CDMA) + return 10; + + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x16d5) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + g_set_error (error, 0, 0, "Only CDMA modems are currently supported by this plugin."); + return NULL; + } + + if (!existing) { + if (caps & CAP_CDMA) { + modem = mm_modem_anydata_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & CAP_CDMA) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_anydata_init (MMPluginAnydata *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_anydata_class_init (MMPluginAnydataClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-anydata.h b/plugins/mm-plugin-anydata.h new file mode 100644 index 0000000..b71aad3 --- /dev/null +++ b/plugins/mm-plugin-anydata.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_ANYDATA_H +#define MM_PLUGIN_ANYDATA_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_ANYDATA (mm_plugin_anydata_get_type ()) +#define MM_PLUGIN_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydata)) +#define MM_PLUGIN_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydataClass)) +#define MM_IS_PLUGIN_ANYDATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_ANYDATA)) +#define MM_IS_PLUGIN_ANYDATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_ANYDATA)) +#define MM_PLUGIN_ANYDATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_ANYDATA, MMPluginAnydataClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginAnydata; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginAnydataClass; + +GType mm_plugin_anydata_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_ANYDATA_H */ diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c new file mode 100644 index 0000000..e5e2ade --- /dev/null +++ b/plugins/mm-plugin-generic.c @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Dan Williams <dcbw@redhat.com> + */ + +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <time.h> + +#include <gmodule.h> + +#include "mm-plugin-generic.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" +#include "mm-errors.h" +#include "mm-serial-parsers.h" + +G_DEFINE_TYPE (MMPluginGeneric, mm_plugin_generic, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_GENERIC, + MM_PLUGIN_BASE_NAME, MM_PLUGIN_GENERIC_NAME, + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 5; + if (capabilities & CAP_CDMA) + return 5; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path, *driver; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "bluetooth")) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } else { + g_message ("%s: (%s/%s) WARNING: missing udev 'device' file", + mm_plugin_get_name (MM_PLUGIN (base)), + subsys, + name); + } + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_generic_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_generic_init (MMPluginGeneric *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_generic_class_init (MMPluginGenericClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} + diff --git a/plugins/mm-plugin-generic.h b/plugins/mm-plugin-generic.h new file mode 100644 index 0000000..8d05689 --- /dev/null +++ b/plugins/mm-plugin-generic.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_GENERIC_H +#define MM_PLUGIN_GENERIC_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_GENERIC (mm_plugin_generic_get_type ()) +#define MM_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGeneric)) +#define MM_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass)) +#define MM_IS_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GENERIC)) +#define MM_IS_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GENERIC)) +#define MM_PLUGIN_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginGeneric; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginGenericClass; + +GType mm_plugin_generic_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_GENERIC_H */ diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c new file mode 100644 index 0000000..77da965 --- /dev/null +++ b/plugins/mm-plugin-gobi.c @@ -0,0 +1,171 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-gobi.h" +#include "mm-modem-gobi-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginGobi, mm_plugin_gobi, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_GOBI, + MM_PLUGIN_BASE_NAME, "Gobi", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *driver; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "qcserial")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_gobi_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_gobi_init (MMPluginGobi *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_gobi_class_init (MMPluginGobiClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} + diff --git a/plugins/mm-plugin-gobi.h b/plugins/mm-plugin-gobi.h new file mode 100644 index 0000000..aceba6b --- /dev/null +++ b/plugins/mm-plugin-gobi.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_GOBI_H +#define MM_PLUGIN_GOBI_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_GOBI (mm_plugin_gobi_get_type ()) +#define MM_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobi)) +#define MM_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass)) +#define MM_IS_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GOBI)) +#define MM_IS_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GOBI)) +#define MM_PLUGIN_GOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginGobi; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginGobiClass; + +GType mm_plugin_gobi_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_GOBI_H */ + diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c new file mode 100644 index 0000000..8493c9c --- /dev/null +++ b/plugins/mm-plugin-hso.c @@ -0,0 +1,182 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-hso.h" +#include "mm-modem-hso.h" + +G_DEFINE_TYPE (MMPluginHso, mm_plugin_hso, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HSO, + MM_PLUGIN_BASE_NAME, "Option High-Speed", + NULL)); +} + +/*****************************************************************************/ + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *driver, *subsys; + + port = mm_plugin_base_supports_task_get_port (task); + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "hso")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + if (!strcmp (subsys, "net")) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + char *devfile; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + devfile = g_strdup (g_udev_device_get_device_file (port)); + if (!devfile) { + if (!strcmp (subsys, "net")) { + /* Apparently 'hso' doesn't set up the right links for the netdevice, + * and thus libgudev can't get the sysfs file path for it. + */ + devfile = g_strdup_printf ("/sys/class/net/%s", name); + if (!g_file_test (devfile, G_FILE_TEST_EXISTS)) { + g_free (devfile); + devfile = NULL; + } + } + + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + goto out; + } + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + goto out; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net")) + goto out; + + if (!existing) { + modem = mm_modem_hso_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + +out: + g_free (devfile); + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_hso_init (MMPluginHso *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_hso_class_init (MMPluginHsoClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-hso.h b/plugins/mm-plugin-hso.h new file mode 100644 index 0000000..a3f4caf --- /dev/null +++ b/plugins/mm-plugin-hso.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_HSO_H +#define MM_PLUGIN_HSO_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_HSO (mm_plugin_hso_get_type ()) +#define MM_PLUGIN_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HSO, MMPluginHso)) +#define MM_PLUGIN_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HSO, MMPluginHsoClass)) +#define MM_IS_PLUGIN_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HSO)) +#define MM_IS_PLUGIN_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HSO)) +#define MM_PLUGIN_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HSO, MMPluginHsoClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginHso; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginHsoClass; + +GType mm_plugin_hso_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_HSO_H */ diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c new file mode 100644 index 0000000..ad799f0 --- /dev/null +++ b/plugins/mm-plugin-huawei.c @@ -0,0 +1,341 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-huawei.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" +#include "mm-modem-huawei-gsm.h" +#include "mm-modem-huawei-cdma.h" +#include "mm-serial-parsers.h" + +G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HUAWEI, + MM_PLUGIN_BASE_NAME, "Huawei", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +#define TAG_SUPPORTS_INFO "huawei-supports-info" + +typedef struct { + MMSerialPort *serial; + guint id; + gboolean secondary; +} HuaweiSupportsInfo; + +static void +huawei_supports_info_destroy (gpointer user_data) +{ + HuaweiSupportsInfo *info = user_data; + + if (info->id) + g_source_remove (info->id); + if (info->serial) + g_object_unref (info->serial); + memset (info, 0, sizeof (HuaweiSupportsInfo)); + g_free (info); +} + +static gboolean +probe_secondary_supported (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = user_data; + HuaweiSupportsInfo *info; + + info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); + + info->id = 0; + g_object_unref (info->serial); + info->serial = NULL; + + /* Yay, supported, we got an unsolicited message */ + info->secondary = TRUE; + mm_plugin_base_supports_task_complete (task, 10); + return FALSE; +} + +static void +probe_secondary_handle_msg (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMPluginBaseSupportsTask *task = user_data; + HuaweiSupportsInfo *info; + + info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); + g_source_remove (info->id); + info->id = g_idle_add (probe_secondary_supported, task); +} + +static gboolean +probe_secondary_timeout (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = user_data; + HuaweiSupportsInfo *info; + + info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); + info->id = 0; + g_object_unref (info->serial); + info->serial = NULL; + + /* Not supported by this plugin */ + mm_plugin_base_supports_task_complete (task, 0); + return FALSE; +} + +static void +add_regex (MMSerialPort *port, const char *match, gpointer user_data) +{ + GRegex *regex; + + regex = g_regex_new (match, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (port, regex, probe_secondary_handle_msg, user_data, NULL); + g_regex_unref (regex); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + int usbif; + guint16 vendor = 0, product = 0; + guint32 existing_type = MM_MODEM_TYPE_UNKNOWN; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x12d1) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM"); + if (usbif < 0) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* The secondary ports don't necessarily respond correctly to probing, so + * we need to use the first port that does respond to probing to create the + * right type of mode (GSM or CDMA), and then re-check the other interfaces. + */ + if (!existing && usbif != 0) + return MM_PLUGIN_SUPPORTS_PORT_DEFER; + + /* CDMA devices don't have problems with the secondary ports, so after + * ensuring we have a device by probing the first port, probe the secondary + * ports on CDMA devices too. + */ + if (existing) + g_object_get (G_OBJECT (existing), MM_MODEM_TYPE, &existing_type, NULL); + + if (usbif == 0 || (existing_type == MM_MODEM_TYPE_CDMA)) { + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } else { + HuaweiSupportsInfo *info; + GError *error = NULL; + + /* Listen for Huawei-specific unsolicited messages */ + info = g_malloc0 (sizeof (HuaweiSupportsInfo)); + + info->serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY); + g_object_set (G_OBJECT (info->serial), MM_PORT_CARRIER_DETECT, FALSE, NULL); + + mm_serial_port_set_response_parser (info->serial, + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + add_regex (info->serial, "\\r\\n\\^RSSI:(\\d+)\\r\\n", task); + add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task); + add_regex (info->serial, "\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", task); + add_regex (info->serial, "\\r\\n\\^BOOT:.+\\r\\n", task); + add_regex (info->serial, "\\r\\r\\^BOOT:.+\\r\\r", task); + + info->id = g_timeout_add (5000, probe_secondary_timeout, task); + + g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO, + info, huawei_supports_info_destroy); + + if (!mm_serial_port_open (info->serial, &error)) { + g_warning ("%s: (Huawei) %s: couldn't open serial port: (%d) %s", + __func__, name, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + huawei_supports_info_destroy (info); + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + guint16 product = 0; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, NULL, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + if (product == 0x1001) { + /* This modem is handled by generic GSM driver */ + modem = mm_generic_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else { + modem = mm_modem_huawei_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } + } else if (caps & CAP_CDMA) { + modem = mm_modem_huawei_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + HuaweiSupportsInfo *info; + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); + if (info && info->secondary && (product != 0x1001)) + ptype = MM_PORT_TYPE_SECONDARY; + + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_huawei_init (MMPluginHuawei *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_huawei_class_init (MMPluginHuaweiClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-huawei.h b/plugins/mm-plugin-huawei.h new file mode 100644 index 0000000..de9294c --- /dev/null +++ b/plugins/mm-plugin-huawei.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_HUAWEI_H +#define MM_PLUGIN_HUAWEI_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_HUAWEI (mm_plugin_huawei_get_type ()) +#define MM_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuawei)) +#define MM_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass)) +#define MM_IS_PLUGIN_HUAWEI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HUAWEI)) +#define MM_IS_PLUGIN_HUAWEI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HUAWEI)) +#define MM_PLUGIN_HUAWEI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HUAWEI, MMPluginHuaweiClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginHuawei; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginHuaweiClass; + +GType mm_plugin_huawei_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_HUAWEI_H */ diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c new file mode 100644 index 0000000..5c19b13 --- /dev/null +++ b/plugins/mm-plugin-longcheer.c @@ -0,0 +1,191 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-longcheer.h" +#include "mm-generic-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginLongcheer, mm_plugin_longcheer, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_LONGCHEER, + MM_PLUGIN_BASE_NAME, "Longcheer", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + guint16 vendor = 0; + const char *subsys, *name; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x1c9e) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + /* Look for port type hints; just probing can't distinguish which port should + * be the data/primary port on these devices. We have to tag them based on + * what the Windows .INF files say the port layout should be. + */ + if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_MODEM")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_AUX")) + ptype = MM_PORT_TYPE_SECONDARY; + + /* If the device was tagged by the udev rules, then ignore any other ports + * to guard against race conditions if a device just happens to show up + * with more than two AT-capable ports. + */ + if ( (ptype == MM_PORT_TYPE_UNKNOWN) + && g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_TAGGED")) + ptype = MM_PORT_TYPE_IGNORED; + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_generic_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_longcheer_init (MMPluginLongcheer *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_longcheer_class_init (MMPluginLongcheerClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-longcheer.h b/plugins/mm-plugin-longcheer.h new file mode 100644 index 0000000..387a290 --- /dev/null +++ b/plugins/mm-plugin-longcheer.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_LONGCHEER_H +#define MM_PLUGIN_LONGCHEER_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_LONGCHEER (mm_plugin_longcheer_get_type ()) +#define MM_PLUGIN_LONGCHEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_LONGCHEER, MMPluginLongcheer)) +#define MM_PLUGIN_LONGCHEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_LONGCHEER, MMPluginLongcheerClass)) +#define MM_IS_PLUGIN_LONGCHEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_LONGCHEER)) +#define MM_IS_PLUGIN_LONGCHEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_LONGCHEER)) +#define MM_PLUGIN_LONGCHEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_LONGCHEER, MMPluginLongcheerClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginLongcheer; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginLongcheerClass; + +GType mm_plugin_longcheer_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_LONGCHEER_H */ diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c new file mode 100644 index 0000000..a380f98 --- /dev/null +++ b/plugins/mm-plugin-mbm.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * Author: Per Hallsmark <per.hallsmark@ericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <string.h> +#include <gmodule.h> + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-mbm.h" +#include "mm-modem-mbm.h" + +G_DEFINE_TYPE (MMPluginMbm, mm_plugin_mbm, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_MBM, + MM_PLUGIN_BASE_NAME, "Ericsson MBM", + NULL)); +} + +/*****************************************************************************/ + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port, *physdev; + guint32 cached = 0, level; + const char *driver, *subsys; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + + if (strcmp (subsys, "tty") && strcmp (subsys, "net")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + if (!g_udev_device_get_property_as_boolean (physdev, "ID_MM_ERICSSON_MBM")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (!strcmp (subsys, "net")) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!(caps & MM_PLUGIN_BASE_PORT_CAP_GSM) && strcmp (subsys, "net")) + return NULL; + + if (!existing) { + modem = mm_modem_mbm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_mbm_init (MMPluginMbm *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_mbm_class_init (MMPluginMbmClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-mbm.h b/plugins/mm-plugin-mbm.h new file mode 100644 index 0000000..c0e73b5 --- /dev/null +++ b/plugins/mm-plugin-mbm.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (C) 2008 Ericsson AB + * + * Author: Per Hallsmark <per.hallsmark@ericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef MM_PLUGIN_MBM_H +#define MM_PLUGIN_MBM_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_MBM (mm_plugin_mbm_get_type ()) +#define MM_PLUGIN_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_MBM, MMPluginMbm)) +#define MM_PLUGIN_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_MBM, MMPluginMbmClass)) +#define MM_IS_PLUGIN_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_MBM)) +#define MM_IS_PLUGIN_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_MBM)) +#define MM_PLUGIN_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MBM, MMPluginMbmClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginMbm; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginMbmClass; + +GType mm_plugin_mbm_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_MBM_H */ diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c new file mode 100644 index 0000000..5b32a1e --- /dev/null +++ b/plugins/mm-plugin-moto-c.c @@ -0,0 +1,160 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin-moto-c.h" +#include "mm-modem-moto-c-gsm.h" + +G_DEFINE_TYPE (MMPluginMotoC, mm_plugin_moto_c, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_MOTO_C, + MM_PLUGIN_BASE_NAME, "MotoC", + NULL)); +} + +/*****************************************************************************/ + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + guint32 level = 0; + + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + level = 10; + + mm_plugin_base_supports_task_complete (task, level); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + const char *tmp; + guint32 cached = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + tmp = g_udev_device_get_property (port, "ID_BUS"); + if (!tmp || strcmp (tmp, "usb")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + tmp = g_udev_device_get_property (port, "ID_VENDOR_ID"); + if (!tmp || strcmp (tmp, "22b8")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + tmp = g_udev_device_get_property (port, "ID_MODEL_ID"); + if (!tmp || (strcmp (tmp, "3802") && strcmp (tmp, "4902"))) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + if (cached & MM_PLUGIN_BASE_PORT_CAP_GSM) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!existing) { + modem = mm_modem_moto_c_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_moto_c_init (MMPluginMotoC *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_moto_c_class_init (MMPluginMotoCClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-moto-c.h b/plugins/mm-plugin-moto-c.h new file mode 100644 index 0000000..8583607 --- /dev/null +++ b/plugins/mm-plugin-moto-c.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_MOTO_C_H +#define MM_PLUGIN_MOTO_C_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_MOTO_C (mm_plugin_moto_c_get_type ()) +#define MM_PLUGIN_MOTO_C(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_MOTO_C, MMPluginMotoC)) +#define MM_PLUGIN_MOTO_C_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_MOTO_C, MMPluginMotoCClass)) +#define MM_IS_PLUGIN_MOTO_C(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_MOTO_C)) +#define MM_IS_PLUGIN_MOTO_C_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_MOTO_C)) +#define MM_PLUGIN_MOTO_C_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MOTO_C, MMPluginMotoCClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginMotoC; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginMotoCClass; + +GType mm_plugin_moto_c_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_MOTO_C_H */ diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c new file mode 100644 index 0000000..e088323 --- /dev/null +++ b/plugins/mm-plugin-nokia.c @@ -0,0 +1,179 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-nokia.h" +#include "mm-modem-nokia.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginNokia, mm_plugin_nokia, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_NOKIA, + MM_PLUGIN_BASE_NAME, "Nokia", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x0421) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_nokia_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_nokia_init (MMPluginNokia *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_nokia_class_init (MMPluginNokiaClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-nokia.h b/plugins/mm-plugin-nokia.h new file mode 100644 index 0000000..fdc0f41 --- /dev/null +++ b/plugins/mm-plugin-nokia.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_NOKIA_H +#define MM_PLUGIN_NOKIA_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_NOKIA (mm_plugin_nokia_get_type ()) +#define MM_PLUGIN_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_NOKIA, MMPluginNokia)) +#define MM_PLUGIN_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_NOKIA, MMPluginNokiaClass)) +#define MM_IS_PLUGIN_NOKIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_NOKIA)) +#define MM_IS_PLUGIN_NOKIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_NOKIA)) +#define MM_PLUGIN_NOKIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_NOKIA, MMPluginNokiaClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginNokia; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginNokiaClass; + +GType mm_plugin_nokia_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_NOKIA_H */ diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c new file mode 100644 index 0000000..48ff7ec --- /dev/null +++ b/plugins/mm-plugin-novatel.c @@ -0,0 +1,183 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-novatel.h" +#include "mm-modem-novatel-gsm.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginNovatel, mm_plugin_novatel, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_NOVATEL, + MM_PLUGIN_BASE_NAME, "Novatel", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name, *driver; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || (strcmp (driver, "option1") && strcmp (driver, "option"))) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x1410 && vendor != 0x413c) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_novatel_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_novatel_init (MMPluginNovatel *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_novatel_class_init (MMPluginNovatelClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-novatel.h b/plugins/mm-plugin-novatel.h new file mode 100644 index 0000000..450bbdd --- /dev/null +++ b/plugins/mm-plugin-novatel.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_NOVATEL_H +#define MM_PLUGIN_NOVATEL_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_NOVATEL (mm_plugin_novatel_get_type ()) +#define MM_PLUGIN_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatel)) +#define MM_PLUGIN_NOVATEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatelClass)) +#define MM_IS_PLUGIN_NOVATEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_NOVATEL)) +#define MM_IS_PLUGIN_NOVATEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_NOVATEL)) +#define MM_PLUGIN_NOVATEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_NOVATEL, MMPluginNovatelClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginNovatel; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginNovatelClass; + +GType mm_plugin_novatel_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_NOVATEL_H */ diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c new file mode 100644 index 0000000..d4c402d --- /dev/null +++ b/plugins/mm-plugin-option.c @@ -0,0 +1,180 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-option.h" +#include "mm-modem-option.h" + +G_DEFINE_TYPE (MMPluginOption, mm_plugin_option, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_OPTION, + MM_PLUGIN_BASE_NAME, "Option", + NULL)); +} + +/*****************************************************************************/ + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *driver, *subsys, *name; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || (strcmp (driver, "option1") && strcmp (driver, "option"))) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x0af0) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + int usbif; + MMPortType ptype = MM_PORT_TYPE_SECONDARY; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + /* This is the MM equivalent of NM commit 9d7f5b3d084eee2ccfff721c4beca3e3f34bdc50; + * Genuine Option NV devices are always supposed to use USB interface 0 as + * the modem/data port, per mail with Option engineers. Only this port + * will emit responses to dialing commands. + */ + usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM"); + if (usbif == 0) + ptype = MM_PORT_TYPE_PRIMARY; + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_option_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_option_init (MMPluginOption *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_option_class_init (MMPluginOptionClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-option.h b/plugins/mm-plugin-option.h new file mode 100644 index 0000000..dfcff74 --- /dev/null +++ b/plugins/mm-plugin-option.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_OPTION_H +#define MM_PLUGIN_OPTION_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_OPTION (mm_plugin_option_get_type ()) +#define MM_PLUGIN_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_OPTION, MMPluginOption)) +#define MM_PLUGIN_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_OPTION, MMPluginOptionClass)) +#define MM_IS_PLUGIN_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_OPTION)) +#define MM_IS_PLUGIN_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_OPTION)) +#define MM_PLUGIN_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_OPTION, MMPluginOptionClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginOption; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginOptionClass; + +GType mm_plugin_option_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_OPTION_H */ diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c new file mode 100644 index 0000000..637f46d --- /dev/null +++ b/plugins/mm-plugin-sierra.c @@ -0,0 +1,204 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-sierra.h" +#include "mm-modem-sierra-gsm.h" +#include "mm-modem-sierra-cdma.h" + +G_DEFINE_TYPE (MMPluginSierra, mm_plugin_sierra, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_SIERRA, + MM_PLUGIN_BASE_NAME, "Sierra", + NULL)); +} + +/*****************************************************************************/ + +#define TAG_SIERRA_SECONDARY_PORT "sierra-secondary-port" + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +handle_probe_response (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + const char *cmd, + const char *response, + const GError *error) +{ + if (error || !response || strcmp (cmd, "I")) { + MM_PLUGIN_BASE_CLASS (mm_plugin_sierra_parent_class)->handle_probe_response (self, task, cmd, response, error); + return; + } + + if (strstr (response, "APP1") || strstr (response, "APP2") || strstr (response, "APP3")) { + g_object_set_data (G_OBJECT (task), TAG_SIERRA_SECONDARY_PORT, GUINT_TO_POINTER (TRUE)); + mm_plugin_base_supports_task_complete (task, 10); + return; + } + + /* Not an app port, let the superclass handle the response */ + MM_PLUGIN_BASE_CLASS (mm_plugin_sierra_parent_class)->handle_probe_response (self, task, cmd, response, error); +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *driver; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + driver = mm_plugin_base_supports_task_get_driver (task); + if (!driver || strcmp (driver, "sierra")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *devfile, *sysfs_path; + guint32 caps; + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + devfile = g_udev_device_get_device_file (port); + if (!devfile) { + g_set_error (error, 0, 0, "Could not get port's sysfs file."); + return NULL; + } + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + /* Is it a GSM secondary port? */ + if (g_object_get_data (G_OBJECT (task), TAG_SIERRA_SECONDARY_PORT)) + ptype = MM_PORT_TYPE_SECONDARY; + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if ((caps & MM_PLUGIN_BASE_PORT_CAP_GSM) || (ptype != MM_PORT_TYPE_UNKNOWN)) { + modem = mm_modem_sierra_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_modem_sierra_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if ( (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) + || (ptype != MM_PORT_TYPE_UNKNOWN)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_sierra_init (MMPluginSierra *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_sierra_class_init (MMPluginSierraClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; + pb_class->handle_probe_response = handle_probe_response; +} diff --git a/plugins/mm-plugin-sierra.h b/plugins/mm-plugin-sierra.h new file mode 100644 index 0000000..01fffc4 --- /dev/null +++ b/plugins/mm-plugin-sierra.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_SIERRA_H +#define MM_PLUGIN_SIERRA_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_SIERRA (mm_plugin_sierra_get_type ()) +#define MM_PLUGIN_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIERRA, MMPluginSierra)) +#define MM_PLUGIN_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIERRA, MMPluginSierraClass)) +#define MM_IS_PLUGIN_SIERRA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIERRA)) +#define MM_IS_PLUGIN_SIERRA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIERRA)) +#define MM_PLUGIN_SIERRA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIERRA, MMPluginSierraClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginSierra; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginSierraClass; + +GType mm_plugin_sierra_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_SIERRA_H */ diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c new file mode 100644 index 0000000..101fb46 --- /dev/null +++ b/plugins/mm-plugin-zte.c @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include <string.h> +#include <gmodule.h> +#include "mm-plugin-zte.h" +#include "mm-modem-zte.h" +#include "mm-generic-cdma.h" + +G_DEFINE_TYPE (MMPluginZte, mm_plugin_zte, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_ZTE, + MM_PLUGIN_BASE_NAME, "ZTE", + NULL)); +} + +/*****************************************************************************/ + +#define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + if (capabilities & CAP_CDMA) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + guint16 vendor = 0; + const char *subsys, *name; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (vendor != 0x19d2) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + if (mm_plugin_base_get_cached_port_capabilities (base, port, &cached)) { + level = get_level_for_capabilities (cached); + if (level) { + mm_plugin_base_supports_task_complete (task, level); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } + + /* Otherwise kick off a probe */ + + /* Many ZTE devices will flood the port with "Message waiting" indications + * and eventually fill up the serial buffer and crash. We need to turn off + * that indicator. See NetworkManager commits + * 1235f71b20c92cded4abd976ccc5010649aae1a0 and + * f38ad328acfdc6ce29dd1380602c546b064161ae for more details. + */ + mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, TRUE); + + if (mm_plugin_base_probe_port (base, task, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL, *physdev = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + /* Look for port type hints */ + if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_MODEM")) + ptype = MM_PORT_TYPE_PRIMARY; + else if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_AUX")) + ptype = MM_PORT_TYPE_SECONDARY; + + physdev = mm_plugin_base_supports_task_get_physdev (task); + g_assert (physdev); + sysfs_path = g_udev_device_get_sysfs_path (physdev); + if (!sysfs_path) { + g_set_error (error, 0, 0, "Could not get port's physical device sysfs path."); + return NULL; + } + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_zte_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base))); + } else if (caps & CAP_CDMA) { + modem = mm_generic_cdma_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856), + !!(caps & MM_PLUGIN_BASE_PORT_CAP_IS856_A)); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else { + if (caps & (MM_PLUGIN_BASE_PORT_CAP_GSM | CAP_CDMA)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, ptype, NULL, error)) + return NULL; + } + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_zte_init (MMPluginZte *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_zte_class_init (MMPluginZteClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->supports_port = supports_port; + pb_class->grab_port = grab_port; +} diff --git a/plugins/mm-plugin-zte.h b/plugins/mm-plugin-zte.h new file mode 100644 index 0000000..7a4d4c9 --- /dev/null +++ b/plugins/mm-plugin-zte.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef MM_PLUGIN_ZTE_H +#define MM_PLUGIN_ZTE_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_ZTE (mm_plugin_zte_get_type ()) +#define MM_PLUGIN_ZTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_ZTE, MMPluginZte)) +#define MM_PLUGIN_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_ZTE, MMPluginZteClass)) +#define MM_IS_PLUGIN_ZTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_ZTE)) +#define MM_IS_PLUGIN_ZTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_ZTE)) +#define MM_PLUGIN_ZTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_ZTE, MMPluginZteClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginZte; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginZteClass; + +GType mm_plugin_zte_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_ZTE_H */ |