From 10d15c65fd10614c4085af390a9fbf390ab958b7 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Wed, 5 Feb 2014 08:38:24 +0100 Subject: Imported Upstream version 0.4+git.20110429t103114.863dbca --- plugins/77-mm-ericsson-mbm.rules | 1 + plugins/77-mm-simtech-port-types.rules | 7 +- plugins/Makefile.am | 44 +- plugins/mm-modem-hso.c | 2 +- plugins/mm-modem-huawei-gsm.c | 98 ++- plugins/mm-modem-icera.c | 56 +- plugins/mm-modem-icera.h | 8 +- plugins/mm-modem-samsung-gsm.c | 679 ++++++++++++++++++++ plugins/mm-modem-samsung-gsm.h | 47 ++ plugins/mm-modem-wavecom-gsm.c | 1099 ++++++++++++++++++++++++++++++++ plugins/mm-modem-wavecom-gsm.h | 44 ++ plugins/mm-modem-zte.c | 49 +- plugins/mm-plugin-anydata.c | 2 +- plugins/mm-plugin-generic.c | 2 +- plugins/mm-plugin-gobi.c | 2 +- plugins/mm-plugin-hso.c | 2 +- plugins/mm-plugin-huawei.c | 230 +++---- plugins/mm-plugin-linktop.c | 2 +- plugins/mm-plugin-longcheer.c | 2 +- plugins/mm-plugin-mbm.c | 2 +- plugins/mm-plugin-moto-c.c | 2 +- plugins/mm-plugin-nokia.c | 2 +- plugins/mm-plugin-novatel.c | 2 +- plugins/mm-plugin-option.c | 2 +- plugins/mm-plugin-samsung.c | 157 +++++ plugins/mm-plugin-samsung.h | 45 ++ plugins/mm-plugin-sierra.c | 2 +- plugins/mm-plugin-simtech.c | 2 +- plugins/mm-plugin-wavecom.c | 162 +++++ plugins/mm-plugin-wavecom.h | 47 ++ plugins/mm-plugin-x22x.c | 2 +- plugins/mm-plugin-zte.c | 24 +- 32 files changed, 2613 insertions(+), 214 deletions(-) create mode 100755 plugins/mm-modem-samsung-gsm.c create mode 100755 plugins/mm-modem-samsung-gsm.h create mode 100644 plugins/mm-modem-wavecom-gsm.c create mode 100644 plugins/mm-modem-wavecom-gsm.h create mode 100755 plugins/mm-plugin-samsung.c create mode 100755 plugins/mm-plugin-samsung.h create mode 100644 plugins/mm-plugin-wavecom.c create mode 100644 plugins/mm-plugin-wavecom.h (limited to 'plugins') diff --git a/plugins/77-mm-ericsson-mbm.rules b/plugins/77-mm-ericsson-mbm.rules index 51cc06f..8c03acf 100644 --- a/plugins/77-mm-ericsson-mbm.rules +++ b/plugins/77-mm-ericsson-mbm.rules @@ -28,6 +28,7 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1" # Ericsson F5521gw ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{ID_MM_ERICSSON_MBM}="1" +ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{ID_MM_ERICSSON_MBM}="1" # Sony-Ericsson MD300 ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1" diff --git a/plugins/77-mm-simtech-port-types.rules b/plugins/77-mm-simtech-port-types.rules index 9ec047c..3848f89 100644 --- a/plugins/77-mm-simtech-port-types.rules +++ b/plugins/77-mm-simtech-port-types.rules @@ -3,6 +3,7 @@ # Simtech makes modules that other companies rebrand, like: # # A-LINK 3GU +# SCT UM300 # # Most of these values were scraped from various SimTech-based Windows # driver .inf files. *mdm.inf lists the main command ports, while @@ -18,12 +19,16 @@ GOTO="mm_simtech_port_types_end" LABEL="mm_alink_vendorcheck" SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" +ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1" +ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" +ATTRS{idProduct}=="cefe", ENV{ID_MM_SIMTECH_TAGGED}="1" + ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1" ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1" ATTRS{idProduct}=="9200", ENV{ID_MM_SIMTECH_TAGGED}="1" GOTO="mm_simtech_port_types_end" - LABEL="mm_simtech_port_types_end" diff --git a/plugins/Makefile.am b/plugins/Makefile.am index dd58b94..8eaffb4 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -35,7 +35,9 @@ pkglib_LTLIBRARIES = \ libmm-plugin-anydata.la \ libmm-plugin-simtech.la \ libmm-plugin-x22x.la \ - libmm-plugin-linktop.la + libmm-plugin-linktop.la \ + libmm-plugin-samsung.la \ + libmm-plugin-wavecom.la # Generic @@ -187,6 +189,24 @@ libmm_plugin_sierra_la_LDFLAGS = \ -module \ -avoid-version +# Wavecom (Sierra Airlink) + +libmm_plugin_wavecom_la_SOURCES = \ + mm-plugin-wavecom.c \ + mm-plugin-wavecom.h \ + mm-modem-wavecom-gsm.c \ + mm-modem-wavecom-gsm.h + +libmm_plugin_wavecom_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_wavecom_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + # Novatel libmm_plugin_novatel_la_SOURCES = \ @@ -241,10 +261,12 @@ libmm_plugin_zte_la_CPPFLAGS = \ libmm_plugin_zte_la_LDFLAGS = \ $(GUDEV_LDFLAGS) \ - $(builddir)/libicera-utils.la \ -module \ -avoid-version +libmm_plugin_zte_la_LIBADD = \ + $(builddir)/libicera-utils.la + # Longcheer (and rebranded dongles) libmm_plugin_longcheer_la_SOURCES = \ @@ -335,6 +357,24 @@ libmm_plugin_linktop_la_LDFLAGS = \ -module \ -avoid-version +# Samsung modem + +libmm_plugin_samsung_la_SOURCES = \ + mm-plugin-samsung.c \ + mm-plugin-samsung.h \ + mm-modem-samsung-gsm.c \ + mm-modem-samsung-gsm.h + +libmm_plugin_samsung_la_CPPFLAGS = \ + $(MM_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -I$(top_srcdir)/src + +libmm_plugin_samsung_la_LDFLAGS = \ + $(GUDEV_LDFLAGS) \ + -module \ + -avoid-version + udevrulesdir = $(UDEV_BASE_DIR)/rules.d udevrules_DATA = \ 77-mm-ericsson-mbm.rules \ diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c index ff0264a..7081666 100644 --- a/plugins/mm-modem-hso.c +++ b/plugins/mm-modem-hso.c @@ -750,7 +750,7 @@ grab_port (MMModem *modem, goto out; if (MM_IS_AT_SERIAL_PORT (port)) { - g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 10000, NULL); + g_object_set (G_OBJECT (port), MM_SERIAL_PORT_SEND_DELAY, (guint64) 0, NULL); if (ptype == MM_PORT_TYPE_PRIMARY) { GRegex *regex; diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c index 3fc9ae2..df58d53 100644 --- a/plugins/mm-modem-huawei-gsm.c +++ b/plugins/mm-modem-huawei-gsm.c @@ -85,9 +85,7 @@ static BandTable bands[] = { /* 2G second */ { MM_MODEM_GSM_BAND_DCS, 0x00000080 }, { MM_MODEM_GSM_BAND_EGSM, 0x00000300 }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */ - { MM_MODEM_GSM_BAND_PCS, 0x00200000 }, - /* And ANY last since it's most inclusive */ - { MM_MODEM_GSM_BAND_ANY, 0x3FFFFFFF }, + { MM_MODEM_GSM_BAND_PCS, 0x00200000 } }; static gboolean @@ -95,13 +93,19 @@ band_mm_to_huawei (MMModemGsmBand band, guint32 *out_huawei) { int i; - for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { - if (bands[i].mm == band) { - *out_huawei = bands[i].huawei; - return TRUE; + /* Treat ANY as a special case: All huawei flags enabled */ + if (band == MM_MODEM_GSM_BAND_ANY) { + *out_huawei = 0x3FFFFFFF; + return TRUE; + } + + *out_huawei = 0; + for (i = 0; i < G_N_ELEMENTS (bands); i++) { + if (bands[i].mm & band) { + *out_huawei |= bands[i].huawei; } } - return FALSE; + return (*out_huawei > 0 ? TRUE : FALSE); } static gboolean @@ -109,16 +113,13 @@ band_huawei_to_mm (guint32 huawei, MMModemGsmBand *out_mm) { int i; - for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { - /* The dongle returns a bitfield, but since we don't support that - * yet in MM, take the "best" band and return it. - */ + *out_mm = 0; + for (i = 0; i < G_N_ELEMENTS (bands); i++) { if (bands[i].huawei & huawei) { - *out_mm = bands[i].mm; - return TRUE; + *out_mm |= bands[i].mm; } } - return FALSE; + return (*out_mm > 0 ? TRUE : FALSE); } /*****************************************************************************/ @@ -687,6 +688,71 @@ handle_status_change (MMAtSerialPort *port, /*****************************************************************************/ +static void +do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + if (!error) { + MMAtSerialPort *primary; + + /* Enable unsolicited result codes */ + primary = mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + mm_at_serial_port_queue_command (primary, "^CURC=1", 5, NULL, NULL); + } + + /* Chain up to parent */ + MM_GENERIC_GSM_CLASS (mm_modem_huawei_gsm_parent_class)->do_enable_power_up_done (gsm, NULL, error, info); +} + +/*****************************************************************************/ + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) + +{ + MMModem *parent_modem_iface; + DisableInfo *info = user_data; + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, info->callback, info->user_data); + g_free (info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMAtSerialPort *primary; + DisableInfo *info; + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Turn off unsolicited responses */ + mm_at_serial_port_queue_command (primary, "^CURC=0", 5, disable_unsolicited_done, info); +} + +/*****************************************************************************/ + static gboolean grab_port (MMModem *modem, const char *subsys, @@ -766,6 +832,7 @@ static void modem_init (MMModem *modem_class) { modem_class->grab_port = grab_port; + modem_class->disable = disable; } static void @@ -798,5 +865,6 @@ mm_modem_huawei_gsm_class_init (MMModemHuaweiGsmClass *klass) gsm_class->set_allowed_mode = set_allowed_mode; gsm_class->get_allowed_mode = get_allowed_mode; gsm_class->get_access_technology = get_access_technology; + gsm_class->do_enable_power_up_done = do_enable_power_up_done; } diff --git a/plugins/mm-modem-icera.c b/plugins/mm-modem-icera.c index 3f71c5b..d34e71e 100644 --- a/plugins/mm-modem-icera.c +++ b/plugins/mm-modem-icera.c @@ -45,7 +45,7 @@ struct _MMModemIceraPrivate { MMModemGsmAccessTech last_act; }; -#define MM_MODEM_ICERA_GET_PRIVATE(m) (MM_MODEM_ICERA_GET_INTERFACE(m)->priv) +#define MM_MODEM_ICERA_GET_PRIVATE(m) (MM_MODEM_ICERA_GET_INTERFACE (m)->get_private(m)) static void get_allowed_mode_done (MMAtSerialPort *port, @@ -172,12 +172,16 @@ static MMModemGsmAccessTech nwstate_to_act (const char *str) { /* small 'g' means CS, big 'G' means PS */ - if (!strcmp (str, "2G-GPRS")) + if (!strcmp (str, "2g")) + return MM_MODEM_GSM_ACCESS_TECH_GSM; + else if (!strcmp (str, "2G-GPRS")) return MM_MODEM_GSM_ACCESS_TECH_GPRS; else if (!strcmp (str, "2G-EDGE")) return MM_MODEM_GSM_ACCESS_TECH_EDGE; else if (!strcmp (str, "3G")) return MM_MODEM_GSM_ACCESS_TECH_UMTS; + else if (!strcmp (str, "3g")) + return MM_MODEM_GSM_ACCESS_TECH_UMTS; else if (!strcmp (str, "3G-HSDPA")) return MM_MODEM_GSM_ACCESS_TECH_HSDPA; else if (!strcmp (str, "3G-HSUPA")) @@ -447,10 +451,10 @@ icera_connect_timed_out (gpointer data) } static void -icera_enabled (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) +icera_connected (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; @@ -478,7 +482,7 @@ old_context_clear_done (MMAtSerialPort *port, MMCallbackInfo *info = (MMCallbackInfo *) user_data; /* Activate the PDP context and start the data session */ - icera_call_control (MM_MODEM_ICERA (info->modem), TRUE, icera_enabled, info); + icera_call_control (MM_MODEM_ICERA (info->modem), TRUE, icera_connected, info); } static void @@ -526,8 +530,8 @@ mm_modem_icera_do_connect (MMModemIcera *self, else { command = g_strdup_printf ("%%IPDPCFG=%d,0,1,\"%s\",\"%s\"", cid, - priv->password ? priv->password : "", - priv->username ? priv->username : ""); + priv->username ? priv->username : "", + priv->password ? priv->password : ""); } @@ -671,7 +675,7 @@ mm_modem_icera_register_unsolicted_handlers (MMModemIcera *self, GRegex *regex; /* %NWSTATE: ,,,, */ - regex = g_regex_new ("\\r\\n%NWSTATE:\\s*(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + regex = g_regex_new ("\\r\\n%NWSTATE:\\s*(-?\\d+),(\\d+),([^,]*),([^,]*),(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); mm_at_serial_port_add_unsolicited_msg_handler (port, regex, nwstate_changed, self, NULL); g_regex_unref (regex); @@ -731,14 +735,6 @@ mm_modem_icera_is_icera (MMModemIcera *self, mm_at_serial_port_queue_command (port, "%IPSYS?", 5, is_icera_done, info); } -/****************************************************************/ - -void -mm_modem_icera_prepare (MMModemIcera *self) -{ - self->priv = g_malloc0 (sizeof (MMModemIceraPrivate)); -} - void mm_modem_icera_cleanup (MMModemIcera *self) { @@ -748,20 +744,32 @@ mm_modem_icera_cleanup (MMModemIcera *self) connect_pending_done (self); g_free (priv->username); + priv->username = NULL; g_free (priv->password); + priv->password = NULL; +} - memset (priv, 0, sizeof (MMModemIceraPrivate)); +/****************************************************************/ + +MMModemIceraPrivate * +mm_modem_icera_init_private (void) +{ + return g_malloc0 (sizeof (MMModemIceraPrivate)); +} + +void +mm_modem_icera_dispose_private (MMModemIcera *self) +{ + MMModemIceraPrivate *priv = MM_MODEM_ICERA_GET_PRIVATE (self); + + mm_modem_icera_cleanup (self); + memset (priv, 0, sizeof (*priv)); g_free (priv); } static void mm_modem_icera_init (gpointer g_iface) { - static gboolean initialized = FALSE; - - if (!initialized) { - initialized = TRUE; - } } GType diff --git a/plugins/mm-modem-icera.h b/plugins/mm-modem-icera.h index 71258d2..aefa841 100644 --- a/plugins/mm-modem-icera.h +++ b/plugins/mm-modem-icera.h @@ -37,12 +37,16 @@ typedef struct _MMModemIcera MMModemIcera; struct _MMModemIcera { GTypeInterface g_iface; - MMModemIceraPrivate *priv; + /* Returns the implementing object's pointer to an internal + * MMModemIceraPrivate pointer. + */ + MMModemIceraPrivate * (*get_private) (MMModemIcera *icera); }; GType mm_modem_icera_get_type (void); -void mm_modem_icera_prepare (MMModemIcera *self); +MMModemIceraPrivate *mm_modem_icera_init_private (void); +void mm_modem_icera_dispose_private (MMModemIcera *self); void mm_modem_icera_cleanup (MMModemIcera *self); diff --git a/plugins/mm-modem-samsung-gsm.c b/plugins/mm-modem-samsung-gsm.c new file mode 100755 index 0000000..4902e0a --- /dev/null +++ b/plugins/mm-modem-samsung-gsm.c @@ -0,0 +1,679 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. + * Copyright (C) 2011 Samsung Electronics, Inc., + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mm-modem-samsung-gsm.h" +#include "mm-modem-simple.h" +#include "mm-errors.h" +#include "mm-callback-info.h" +#include "mm-modem-gsm-card.h" +#include "mm-log.h" +#include "mm-modem-icera.h" +#include "mm-utils.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); +static void modem_simple_init (MMModemSimple *class); +static void modem_gsm_card_init (MMModemGsmCard *class); +static void modem_icera_init (MMModemIcera *icera_class); + +G_DEFINE_TYPE_EXTENDED (MMModemSamsungGsm, mm_modem_samsung_gsm, MM_TYPE_GENERIC_GSM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_ICERA, modem_icera_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_CARD, modem_gsm_card_init)) + +#define MM_MODEM_SAMSUNG_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsmPrivate)) + +typedef struct { + gboolean disposed; + + MMModemIceraPrivate *icera; + char *band; +} MMModemSamsungGsmPrivate; + +#define IPDPADDR_TAG "%IPDPADDR: " + + +MMModem * +mm_modem_samsung_gsm_new (const char *device, + const char *driver, + const char *plugin) +{ + MMModem *modem; + + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + modem = MM_MODEM (g_object_new (MM_TYPE_MODEM_SAMSUNG_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_IP_METHOD, MM_MODEM_IP_METHOD_DHCP, + NULL)); + if (modem) + MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (modem)->icera = mm_modem_icera_init_private (); + + return modem; +} + +typedef struct { + MMModemGsmBand mm; + char band[50]; +} BandTable; + +static BandTable bands[12] = { + /* Sort 3G first since it's preferred */ + { MM_MODEM_GSM_BAND_U2100, "FDD_BAND_I" }, + { MM_MODEM_GSM_BAND_U1900, "FDD_BAND_II" }, + { MM_MODEM_GSM_BAND_U1800, "FDD_BAND_III" }, + { MM_MODEM_GSM_BAND_U17IV, "FDD_BAND_IV" }, + { MM_MODEM_GSM_BAND_U850, "FDD_BAND_V" }, + { MM_MODEM_GSM_BAND_U800, "FDD_BAND_VI" }, + { MM_MODEM_GSM_BAND_U900, "FDD_BAND_VIII" }, + { MM_MODEM_GSM_BAND_G850, "G850" }, + /* 2G second */ + { MM_MODEM_GSM_BAND_DCS, "DCS" }, + { MM_MODEM_GSM_BAND_EGSM, "EGSM" }, /* 0x100 = Extended GSM, 0x200 = Primary GSM */ + { MM_MODEM_GSM_BAND_PCS, "PCS" }, + /* And ANY last since it's most inclusive */ + { MM_MODEM_GSM_BAND_ANY, "ANY" }, +}; + +static gboolean +band_mm_to_samsung (MMModemGsmBand band, MMModemGsmNetwork *modem) +{ + int i; + MMModemSamsungGsmPrivate *priv = MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (modem); + + for (i = 0; i < sizeof (bands) / sizeof (BandTable); i++) { + if (bands[i].mm == band) { + priv->band = bands[i].band; + return TRUE; + } + } + return FALSE; +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + mm_modem_icera_get_allowed_mode (MM_MODEM_ICERA (gsm), callback, user_data); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + mm_modem_icera_set_allowed_mode (MM_MODEM_ICERA (gsm), mode, callback, user_data); +} + +static void +set_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + if (error) + info->error = g_error_copy (error); + + mm_callback_info_schedule (info); +} + +static void +set_band (MMModemGsmNetwork *modem, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + char *command; + MMModemSamsungGsmPrivate *priv = MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (modem); + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* TODO: Check how to pass more than one band in the same AT%%IPBM command */ + if (!utils_check_for_single_value (band)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Cannot set more than one band."); + mm_callback_info_schedule (info); + } else if (!band_mm_to_samsung (band, modem)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid band."); + mm_callback_info_schedule (info); + } else { + mm_callback_info_set_data (info, "band", g_strdup(priv->band), NULL); + command = g_strdup_printf ("AT%%IPBM=\"%s\",1", priv->band); + mm_at_serial_port_queue_command (port, command, 3, set_band_done, info); + g_free (command); + priv->band = NULL; + } +} + +static gboolean +parse_ipbm (const char *reply, MMModemGsmBand *band) +{ + int enable[12]; + + g_return_val_if_fail (band != NULL, FALSE); + g_return_val_if_fail (reply != NULL, FALSE); + + if (sscanf (reply, "\"ANY\": %d\r\n\"EGSM\": %d\r\n\"DCS\": %d\r\n\"PCS\": %d\r\n\"G850\": %d\r\n\"FDD_BAND_I\": %d\r\n\"FDD_BAND_II\": %d\r\n\"FDD_BAND_III\": %d\r\n\"FDD_BAND_IV\": %d\r\n\"FDD_BAND_V\": %d\r\n\"FDD_BAND_VI\": %d\r\n\"FDD_BAND_VIII\": %d", &enable[0], &enable[1], &enable[2], &enable[3], &enable[4], &enable[5], &enable[6], &enable[7], &enable[8], &enable[9], &enable[10], &enable[11]) != 12) + return FALSE; + + *band = 0; + if (enable[5] == 1) + *band |= MM_MODEM_GSM_BAND_U2100; + if (enable[6] == 1) + *band |= MM_MODEM_GSM_BAND_U1900; + if (enable[7] == 1) + *band |= MM_MODEM_GSM_BAND_U1800; + if (enable[8] == 1) + *band |= MM_MODEM_GSM_BAND_U17IV; + if (enable[9] == 1) + *band |= MM_MODEM_GSM_BAND_U850; + if (enable[10] == 1) + *band |= MM_MODEM_GSM_BAND_U800; + if (enable[11] == 1) + *band |= MM_MODEM_GSM_BAND_U900; + if (enable[1] == 1) + *band |= MM_MODEM_GSM_BAND_EGSM; + if (enable[2] == 1) + *band |= MM_MODEM_GSM_BAND_DCS; + if (enable[3] == 1) + *band |= MM_MODEM_GSM_BAND_PCS; + if (enable[4] == 1) + *band |= MM_MODEM_GSM_BAND_G850; + + return (*band > 0 ? TRUE : FALSE); +} + +static void +get_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemGsmBand mm_band = MM_MODEM_GSM_BAND_UNKNOWN; + + if (error) + info->error = g_error_copy (error); + else if (parse_ipbm (response->str, &mm_band)) + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + + mm_callback_info_schedule (info); +} + +static void +get_band (MMModemGsmNetwork *modem, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "AT%IPBM?", 3, get_band_done, info); +} + +static gboolean +parse_samsung_num (const char *str, guint32 *out_num, guint32 min, guint32 max) +{ + unsigned long int tmp; + + if (!str || !strlen (str)) + return FALSE; + + errno = 0; + tmp = strtoul (str, NULL, 10); + if (errno != 0 || tmp < min || tmp > max) + return FALSE; + *out_num = (guint32) tmp; + return TRUE; +} + +static void +send_samsung_pinnum_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + GRegex *r = NULL; + GMatchInfo *match_info = NULL; + const char *pin_type; + guint32 attempts_left = 0; + char *str = NULL; + guint32 num = 0; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + pin_type = mm_callback_info_get_data (info, "pin_type"); + + r = g_regex_new ("\\%PINNUM:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)", G_REGEX_UNGREEDY, 0, NULL); + if (!r) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse %PINNUM results (error creating regex)."); + goto done; + } + + if (!g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error)) { + g_set_error_literal (&info->error, + MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Could not parse %PINNUM results (match failed)."); + goto done; + } + + if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN)) + num = 1; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK)) + num = 2; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PIN2)) + num = 3; + else if (strstr (pin_type, MM_MODEM_GSM_CARD_SIM_PUK2)) + num = 4; + else { + info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Unhandled PIN type"); + } + + if (num > 0) { + gboolean success = FALSE; + + str = g_match_info_fetch (match_info, num); + if (str) { + success = parse_samsung_num (str, &attempts_left, 0, 10); + g_free (str); + } + + if (!success) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse %PINNUM results (missing or invalid match info)."); + } + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (attempts_left), NULL); + + g_match_info_free (match_info); + +done: + if (r) + g_regex_unref (r); + mm_serial_port_close (MM_SERIAL_PORT (port)); + mm_callback_info_schedule (info); +} + +static void +reset (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (port) + mm_at_serial_port_queue_command (port, "%IRESET", 3, NULL, NULL); + + mm_callback_info_schedule (info); +} + +static void +get_unlock_retries (MMModemGsmCard *modem, + const char *pin_type, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data); + + mm_dbg ("pin type '%s'", pin_type); + + /* Ensure we have a usable port to use for the command */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + /* Modem may not be enabled yet, which sometimes can't be done until + * the device has been unlocked. In this case we have to open the port + * ourselves. + */ + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &info->error)) { + mm_callback_info_schedule (info); + return; + } + + /* if the modem have not yet been enabled we need to make sure echoing is turned off */ + mm_at_serial_port_queue_command (port, "E0", 3, NULL, NULL); + mm_callback_info_set_data (info, "pin_type", g_strdup (pin_type), g_free); + mm_at_serial_port_queue_command (port, "%PINNUM?", 3, send_samsung_pinnum_done, info); + +} + +static void +get_access_technology (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + mm_modem_icera_get_access_technology (MM_MODEM_ICERA (gsm), callback, user_data); +} + +typedef struct { + MMModem *modem; + MMModemFn callback; + gpointer user_data; +} DisableInfo; + +static void +disable_unsolicited_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) + +{ + MMModem *parent_modem_iface; + DisableInfo *info = user_data; + + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (info->modem)); + parent_modem_iface->disable (info->modem, info->callback, info->user_data); + g_free (info); +} + +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMAtSerialPort *primary; + DisableInfo *info; + + mm_modem_icera_cleanup (MM_MODEM_ICERA (modem)); + mm_modem_icera_change_unsolicited_messages (MM_MODEM_ICERA (modem), FALSE); + + info = g_malloc0 (sizeof (DisableInfo)); + info->callback = callback; + info->user_data = user_data; + info->modem = modem; + + primary = mm_generic_gsm_get_at_port (MM_GENERIC_GSM (modem), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + /* Random command to ensure unsolicited message disable completes */ + mm_at_serial_port_queue_command (primary, "AT+CFUN=0", 5, disable_unsolicited_done, info); +} + +static void +init_all_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (info->modem); + + if (!error) + mm_modem_icera_change_unsolicited_messages (MM_MODEM_ICERA (self), TRUE); + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); +} + +static void +init2_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (info->modem); + + if (error) + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); + else { + /* Finish the initialization */ + mm_at_serial_port_queue_command (port, "E0 V1 X4 &C1", 3, init_all_done, info); + } +} + +static void +init_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (info->modem); + + if (error) + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (self), error, info); + else { + /* Power up the modem */ + mm_at_serial_port_queue_command (port, "+CMEE=1", 2, NULL, NULL); + mm_at_serial_port_queue_command (port, "+CFUN=1", 10, init2_done, info); + } +} + +static void +do_enable (MMGenericGsm *modem, MMModemFn callback, gpointer user_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *primary; + + info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); + + primary = mm_generic_gsm_get_at_port (modem, MM_PORT_TYPE_PRIMARY); + g_assert (primary); + mm_at_serial_port_queue_command (primary, "Z E0 V1", 3, init_done, info); +} + +static void +do_connect (MMModem *modem, + const char *number, + MMModemFn callback, + gpointer user_data) +{ + mm_modem_icera_do_connect (MM_MODEM_ICERA (modem), number, callback, user_data); +} + +static void +do_disconnect (MMGenericGsm *gsm, + gint cid, + MMModemFn callback, + gpointer user_data) +{ + mm_modem_icera_do_disconnect (gsm, cid, callback, user_data); +} + +static void +get_ip4_config (MMModem *modem, + MMModemIp4Fn callback, + gpointer user_data) +{ + mm_modem_icera_get_ip4_config (MM_MODEM_ICERA (modem), callback, user_data); +} + +static void +simple_connect (MMModemSimple *simple, + GHashTable *properties, + MMModemFn callback, + gpointer user_data) +{ + MMModemSimple *parent_iface; + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + + /* Let icera cache user & password */ + mm_modem_icera_simple_connect (MM_MODEM_ICERA (simple), properties); + + parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); + parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + 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 (!strcmp (subsys, "tty")) { + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_AT_SERIAL_PORT (port)) { + g_object_set (port, + MM_PORT_CARRIER_DETECT, FALSE, + MM_SERIAL_PORT_SEND_DELAY, (guint64) 0, + NULL); + + /* Add Icera-specific handlers */ + mm_modem_icera_register_unsolicted_handlers (MM_MODEM_ICERA (gsm), MM_AT_SERIAL_PORT (port)); + } + + return !!port; +} + +static MMModemIceraPrivate * +get_icera_private (MMModemIcera *icera) +{ + return MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (icera)->icera; +} + +static void +modem_init (MMModem *modem_class) +{ + modem_class->reset = reset; + modem_class->disable = disable; + modem_class->connect = do_connect; + modem_class->get_ip4_config = get_ip4_config; + modem_class->grab_port = grab_port; +} + +static void +modem_icera_init (MMModemIcera *icera) +{ + icera->get_private = get_icera_private; +} + +static void +modem_simple_init (MMModemSimple *class) +{ + class->connect = simple_connect; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *class) +{ + class->set_band = set_band; + class->get_band = get_band; +} + +static void +modem_gsm_card_init (MMModemGsmCard *class) +{ + class->get_unlock_retries = get_unlock_retries; +} + +static void +mm_modem_samsung_gsm_init (MMModemSamsungGsm *self) +{ +} + +static void +dispose (GObject *object) +{ + MMModemSamsungGsm *self = MM_MODEM_SAMSUNG_GSM (object); + MMModemSamsungGsmPrivate *priv = MM_MODEM_SAMSUNG_GSM_GET_PRIVATE (self); + + if (priv->disposed == FALSE) { + priv->disposed = TRUE; + + mm_modem_icera_dispose_private (MM_MODEM_ICERA (self)); + } + + G_OBJECT_CLASS (mm_modem_samsung_gsm_parent_class)->dispose (object); +} + +static void +mm_modem_samsung_gsm_class_init (MMModemSamsungGsmClass *klass) +{ + + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + mm_modem_samsung_gsm_parent_class = g_type_class_peek_parent (klass); + + g_type_class_add_private (object_class, sizeof (MMModemSamsungGsmPrivate)); + + object_class->dispose = dispose; + + gsm_class->do_disconnect = do_disconnect; + gsm_class->do_enable = do_enable; + + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; +} diff --git a/plugins/mm-modem-samsung-gsm.h b/plugins/mm-modem-samsung-gsm.h new file mode 100755 index 0000000..9e50716 --- /dev/null +++ b/plugins/mm-modem-samsung-gsm.h @@ -0,0 +1,47 @@ +/* -*- 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. + * Copyright (C) 2011 Samsung Electronics, Inc., + */ + +#ifndef MM_MODEM_SAMSUNG_GSM_H +#define MM_MODEM_SAMSUNG_GSM_H + +#include + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_SAMSUNG_GSM (mm_modem_samsung_gsm_get_type ()) +#define MM_MODEM_SAMSUNG_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsm)) +#define MM_MODEM_SAMSUNG_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsmClass)) +#define MM_IS_MODEM_SAMSUNG_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SAMSUNG_GSM)) +#define MM_IS_MODEM_SAMSUNG_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_SAMSUNG_GSM)) +#define MM_MODEM_SAMSUNG_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SAMSUNG_GSM, MMModemSamsungGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemSamsungGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemSamsungGsmClass; + +GType mm_modem_samsung_gsm_get_type (void); + +MMModem *mm_modem_samsung_gsm_new (const char *device, + const char *driver, + const char *plugin_name); + +#endif /* MM_MODEM_SAMSUNG_GSM_H */ + diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c new file mode 100644 index 0000000..3c8a49b --- /dev/null +++ b/plugins/mm-modem-wavecom-gsm.c @@ -0,0 +1,1099 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#include + +#include +#include +#include +#include + +#include "mm-errors.h" +#include "mm-modem-helpers.h" +#include "mm-modem-wavecom-gsm.h" +#include "mm-serial-parsers.h" +#include "mm-log.h" + +static void modem_init (MMModem *modem_class); +static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); + +G_DEFINE_TYPE_EXTENDED (MMModemWavecomGsm, mm_modem_wavecom_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)) + +/* Bit flags for mobile station classes supported by the modem */ +typedef enum { + WAVECOM_MS_CLASS_UNKNOWN = 0, + /* Class C in circuit switched only mode, CS */ + WAVECOM_MS_CLASS_CC = 1 << 0, + /* Class C in GPRS only mode, PS */ + WAVECOM_MS_CLASS_CG = 1 << 1, + /* Class B (either CS or PS, not both at the same time) + * This should be the default for GSM/GPRS modems */ + WAVECOM_MS_CLASS_B = 1 << 2, + /* Class A in 3G only mode */ + WAVECOM_MS_CLASS_A = 1 << 3 +} WavecomMSClass; + +#define WAVECOM_MS_CLASS_CC_IDSTR "\"CC\"" +#define WAVECOM_MS_CLASS_CG_IDSTR "\"CG\"" +#define WAVECOM_MS_CLASS_B_IDSTR "\"B\"" +#define WAVECOM_MS_CLASS_A_IDSTR "\"A\"" + +/* Mask of all supported 2G bands */ +#define ALL_2G_BANDS \ + (MM_MODEM_GSM_BAND_EGSM | \ + MM_MODEM_GSM_BAND_DCS | \ + MM_MODEM_GSM_BAND_PCS | \ + MM_MODEM_GSM_BAND_G850) + +/* Mask of all supported 3G bands */ +#define ALL_3G_BANDS \ + (MM_MODEM_GSM_BAND_U2100 | \ + MM_MODEM_GSM_BAND_U1800 | \ + MM_MODEM_GSM_BAND_U17IV | \ + MM_MODEM_GSM_BAND_U800 | \ + MM_MODEM_GSM_BAND_U850 | \ + MM_MODEM_GSM_BAND_U900 | \ + MM_MODEM_GSM_BAND_U17IX | \ + MM_MODEM_GSM_BAND_U1900 | \ + MM_MODEM_GSM_BAND_U2600) + +#define MM_MODEM_WAVECOM_GSM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsmPrivate)) + +typedef struct { + /* Bitmask for supported MS classes */ + guint8 supported_ms_classes; + /* Current MS class */ + WavecomMSClass current_ms_class; + /* Current allowed mode, only for 3G devices */ + MMModemGsmAllowedMode allowed_mode; + /* Bitmask for currently active bands */ + guint32 current_bands; +} MMModemWavecomGsmPrivate; + +/* Setup relationship between 2G bands in the modem (identified by a + * single digit in ASCII) and the bitmask in ModemManager. */ +typedef struct { + gchar wavecom_band; + guint32 mm_band_mask; +} WavecomBand2G; +static const WavecomBand2G bands_2g[] = { + { '0', MM_MODEM_GSM_BAND_G850 }, + { '1', MM_MODEM_GSM_BAND_EGSM }, + { '2', MM_MODEM_GSM_BAND_DCS }, + { '3', MM_MODEM_GSM_BAND_PCS }, + { '4', (MM_MODEM_GSM_BAND_G850 | MM_MODEM_GSM_BAND_PCS) }, + { '5', (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_DCS) }, + { '6', (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_PCS) }, + { '7', ALL_2G_BANDS } +}; + +/* Setup relationship between the 3G band bitmask in the modem and the bitmask + * in ModemManager. */ +typedef struct { + guint32 wavecom_band_flag; + guint32 mm_band_flag; +} WavecomBand3G; +static const WavecomBand3G bands_3g[] = { + { (1 << 0), MM_MODEM_GSM_BAND_U2100 }, + { (1 << 1), MM_MODEM_GSM_BAND_U1900 }, + { (1 << 2), MM_MODEM_GSM_BAND_U1800 }, + { (1 << 3), MM_MODEM_GSM_BAND_U17IV }, + { (1 << 4), MM_MODEM_GSM_BAND_U850 }, + { (1 << 5), MM_MODEM_GSM_BAND_U800 }, + { (1 << 6), MM_MODEM_GSM_BAND_U2600 }, + { (1 << 7), MM_MODEM_GSM_BAND_U900 }, + { (1 << 8), MM_MODEM_GSM_BAND_U17IX } +}; + +MMModem * +mm_modem_wavecom_gsm_new (const char *device, + const char *driver, + const char *plugin, + guint32 vendor, + guint32 product) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + g_return_val_if_fail (plugin != NULL, NULL); + + return MM_MODEM (g_object_new (MM_TYPE_MODEM_WAVECOM_GSM, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, + NULL)); +} + +static const gchar * +wavecom_ms_class_to_str (WavecomMSClass class) +{ + switch (class) { + case WAVECOM_MS_CLASS_CC: + return WAVECOM_MS_CLASS_CC_IDSTR; + case WAVECOM_MS_CLASS_CG: + return WAVECOM_MS_CLASS_CG_IDSTR; + case WAVECOM_MS_CLASS_B: + return WAVECOM_MS_CLASS_B_IDSTR; + case WAVECOM_MS_CLASS_A: + return WAVECOM_MS_CLASS_A_IDSTR; + default: + g_warn_if_reached (); + return NULL; + } +} + +static gboolean +grab_port (MMModem *modem, + const char *subsys, + const char *name, + MMPortType suggested_type, + gpointer user_data, + GError **error) +{ + MMGenericGsm *gsm = MM_GENERIC_GSM (modem); + MMPortType ptype = MM_PORT_TYPE_IGNORED; + MMPort *port = NULL; + + if (suggested_type == MM_PORT_TYPE_UNKNOWN) { + if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_PRIMARY)) + ptype = MM_PORT_TYPE_PRIMARY; + else if (!mm_generic_gsm_get_at_port (gsm, MM_PORT_TYPE_SECONDARY)) + ptype = MM_PORT_TYPE_SECONDARY; + } else + ptype = suggested_type; + + port = mm_generic_gsm_grab_port (gsm, subsys, name, ptype, error); + if (port && MM_IS_AT_SERIAL_PORT (port)) { + gpointer parser; + GRegex *regex; + + parser = mm_serial_parser_v1_new (); + + /* AT+CPIN? replies will never have an OK appended */ + regex = g_regex_new ("\\r\\n\\+CPIN: .*\\r\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, NULL); + mm_serial_parser_v1_set_custom_regex (parser, regex, NULL); + g_regex_unref (regex); + + mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port), + mm_serial_parser_v1_parse, + parser, + mm_serial_parser_v1_destroy); + } + + return !!port; +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + /* Do nothing... see set_property() in parent, which also does nothing */ +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case MM_GENERIC_GSM_PROP_POWER_UP_CMD: + /* Wavecom doesn't like CFUN=1, it will reset the whole software stack, + * including the USB connection and therefore connection would get + * closed */ + g_value_set_string (value, ""); + break; + case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: + /* Wavecom doesn't have XOFF/XON flow control, so we enable RTS/CTS */ + g_value_set_string (value, "+IFC=2,2"); + break; + default: + break; + } +} + +static void +set_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else + priv->current_bands = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-band")); + + mm_callback_info_schedule (info); +} + +static void +set_2g_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMAtSerialPort *port, + MMCallbackInfo *info) +{ + gchar wavecom_band; + gchar *cmd; + guint i; + + /* Ensure we don't get 3G bands when trying to configure 2G bands */ + if (band & ALL_3G_BANDS) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Not allowed to set 3G bands in 2G mode"); + mm_callback_info_schedule (info); + return; + } + + /* If we get ANY, reset to all-2G bands to get the proper value */ + if (band == MM_MODEM_GSM_BAND_ANY) + band = ALL_2G_BANDS; + + /* Loop looking for allowed masks */ + wavecom_band = '\0'; + for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { + if (bands_2g[i].mm_band_mask == band) { + wavecom_band = bands_2g[i].wavecom_band; + break; + } + } + + /* If we didn't find a match, set an error */ + if (wavecom_band == '\0') { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Wrong 2G band mask: '%u'", band); + mm_callback_info_schedule (info); + return; + } + + mm_callback_info_set_data (info, + "new-band", + GUINT_TO_POINTER ((guint)band), + NULL); + + cmd = g_strdup_printf ("+WMBS=%c,1", wavecom_band); + mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info); + g_free (cmd); +} + +static void +set_3g_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMAtSerialPort *port, + MMCallbackInfo *info) +{ + guint wavecom_band; + gchar *cmd; + guint i; + + /* Ensure we don't get 2G bands when trying to configure 2G bands */ + if (band & ALL_2G_BANDS) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Not allowed to set 2G bands in 3G mode"); + mm_callback_info_schedule (info); + return; + } + + /* If we get ANY, reset to all-3G bands to get the proper value */ + if (band == MM_MODEM_GSM_BAND_ANY) + band = ALL_3G_BANDS; + + /* Loop looking for allowed masks */ + wavecom_band = 0; + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { + if (bands_3g[i].mm_band_flag & band) { + wavecom_band |= bands_3g[i].wavecom_band_flag; + } + } + + /* If we didn't find a match, set an error */ + if (wavecom_band == 0) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Wrong 3G band mask: '%u'", band); + mm_callback_info_schedule (info); + return; + } + + mm_callback_info_set_data (info, + "new-band", + GUINT_TO_POINTER ((guint)band), + NULL); + + cmd = g_strdup_printf ("+WMBS=\"%u\",1", wavecom_band); + mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info); + g_free (cmd); +} + +static void +set_band (MMModemGsmNetwork *self, + MMModemGsmBand band, + MMModemFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + + /* Are we trying to change the band to the same bands currently + * being used? if so, we're done */ + if (priv->current_bands == band) { + mm_callback_info_schedule (info); + return; + } + + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) + set_2g_band (self, band, port, info); + else + set_3g_band (self, band, port, info); +} + +static void +get_2g_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else { + const gchar *p; + guint32 mm_band = MM_MODEM_GSM_BAND_UNKNOWN; + + p = mm_strip_tag (response->str, "+WMBS:"); + if (p) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { + if (bands_2g[i].wavecom_band == *p) { + mm_band = bands_2g[i].mm_band_mask; + break; + } + } + } + + if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) { + g_set_error (&info->error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get 2G bands"); + } else { + priv->current_bands = mm_band; + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + } + } + + mm_callback_info_schedule (info); +} + +static void +get_3g_band_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else { + const gchar *p; + guint mm_band = MM_MODEM_GSM_BAND_UNKNOWN; + guint32 wavecom_band; + + /* Example reply: + * AT+WUBS? --> + * <-- +WUBS: "3",1 + * <-- OK + * The "3" meaning here Band I and II are selected. + */ + + p = mm_strip_tag (response->str, "+WUBS:"); + if (*p == '"') + p++; + wavecom_band = atoi (p); + + if (wavecom_band > 0) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { + if (bands_3g[i].wavecom_band_flag & wavecom_band) { + mm_band |= bands_3g[i].mm_band_flag; + } + } + } + + if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) { + g_set_error (&info->error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get 3G bands"); + } else { + priv->current_bands = mm_band; + mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); + } + } + + mm_callback_info_schedule (info); +} + +static void +get_band (MMModemGsmNetwork *self, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); + + /* If results are already cached, return them */ + if (priv->current_bands > 0) { + mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->current_bands), NULL); + mm_callback_info_schedule (info); + return; + } + + /* Otherwise ask the modem */ + port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) + mm_at_serial_port_queue_command (port, "AT+WMBS?", 3, get_2g_band_done, info); + else + mm_at_serial_port_queue_command (port, "AT+WUBS?", 3, get_3g_band_done, info); +} + +static void +get_access_technology_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + const gchar *p; + + if (error) + info->error = g_error_copy (error); + else { + p = mm_strip_tag (response->str, "+WGPRSIND:"); + if (!p) { + g_set_error (&info->error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get network capabilities"); + } else { + switch (*p) { + case '1': + /* GPRS only */ + act = MM_MODEM_GSM_ACCESS_TECH_GPRS; + break; + case '2': + /* EGPRS/EDGE supported */ + act = MM_MODEM_GSM_ACCESS_TECH_EDGE; + break; + case '3': + /* 3G R99 supported */ + act = MM_MODEM_GSM_ACCESS_TECH_UMTS; + break; + case '4': + /* HSDPA supported */ + act = MM_MODEM_GSM_ACCESS_TECH_HSDPA; + break; + case '5': + /* HSUPA supported */ + act = MM_MODEM_GSM_ACCESS_TECH_HSUPA; + break; + default: + break; + } + } + } + + mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); + mm_callback_info_schedule (info); +} + +static void +get_access_technology (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+WGPRS=9,2", 3, get_access_technology_cb, info); +} + +static void +get_allowed_mode_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + gint read_mode = -1; + gchar *mode_str = NULL; + gchar *prefer_str = NULL; + GRegex *r = NULL; + GMatchInfo *match_info = NULL; + + if (error) { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + return; + } + + /* Possible responses: + * +WWSM: 0 (2G only) + * +WWSM: 1 (3G only) + * +WWSM: 2,0 (Any) + * +WWSM: 2,1 (2G preferred) + * +WWSM: 2,2 (3G preferred) + */ + r = g_regex_new ("\\r\\n\\+WWSM: ([0-2])(,([0-2]))?.*$", 0, 0, NULL); + if (r && g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, NULL)) { + mode_str = g_match_info_fetch (match_info, 1); + prefer_str = g_match_info_fetch (match_info, 3); /* 3, to avoid the comma */ + + if (mode_str) { + switch (atoi (mode_str)) { + case 0: + if (!prefer_str) + read_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY; + break; + case 1: + if (!prefer_str) + read_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY; + break; + case 2: + if (prefer_str) { + switch (atoi (prefer_str)) { + case 0: + read_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + break; + case 1: + read_mode = MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED; + break; + case 2: + read_mode = MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED; + break; + default: + g_warn_if_reached (); + break; + } + } + break; + default: + g_warn_if_reached (); + break; + } + } + } + + if (read_mode < 0) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unexpected wireless data service reply: '%s' " + "(mode: '%s', prefer: '%s')", + response->str, + mode_str ? mode_str : "none", + prefer_str ? prefer_str : "none"); + } else { + priv->allowed_mode = (guint)read_mode; + mm_callback_info_set_result (info, + GUINT_TO_POINTER (priv->allowed_mode), + NULL); + } + + if (r) + g_regex_unref (r); + if (match_info) + g_match_info_free (match_info); + g_free (mode_str); + g_free (prefer_str); + + mm_callback_info_schedule (info); +} + +static void +get_allowed_mode (MMGenericGsm *gsm, + MMModemUIntFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (gsm); + MMCallbackInfo *info; + + info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); + + /* For 3G devices, query WWSM status */ + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + MMAtSerialPort *port; + + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "+WWSM?", 3, get_allowed_mode_cb, info); + return; + } + + /* For 2G devices, just return cached value */ + mm_callback_info_set_result (info, + GUINT_TO_POINTER (priv->allowed_mode), + NULL); + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + + if (error) + info->error = g_error_copy (error); + else + priv->allowed_mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-mode")); + + mm_callback_info_schedule (info); +} + +static void +set_allowed_mode (MMGenericGsm *gsm, + MMModemGsmAllowedMode mode, + MMModemFn callback, + gpointer user_data) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (gsm); + MMCallbackInfo *info; + + info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); + + /* For 3G devices, go on with WWSM */ + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + MMAtSerialPort *port; + GString *cmd; + gint net = -1; + gint prefer = -1; + + /* Get port */ + port = mm_generic_gsm_get_best_at_port (gsm, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_callback_info_set_data (info, + "new-mode", + GUINT_TO_POINTER (mode), + NULL); + + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + net = 2; + prefer = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + net = 2; + prefer = 1; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: + net = 2; + prefer = 2; + break; + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + net = 0; + break; + case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: + net = 1; + break; + } + + cmd = g_string_new ("+WWSM="); + g_string_append_printf (cmd, "%d", net); + if (net == 2) + g_string_append_printf (cmd, ",%d", prefer); + mm_at_serial_port_queue_command (port, cmd->str, 3, set_allowed_mode_cb, info); + g_string_free (cmd, TRUE); + + return; + } + + /* For non-3G devices, allow only 2G-related allowed modes */ + switch (mode) { + case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: + case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: + case MM_MODEM_GSM_ALLOWED_MODE_ANY: + priv->allowed_mode = mode; + break; + default: + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Cannot set desired allowed mode, " + "not a 3G device"); + break; + } + + mm_callback_info_schedule (info); +} + +static void +enable_complete (MMGenericGsm *gsm, + GError *error, + MMCallbackInfo *info) +{ + /* Do NOT chain up parent do_enable_power_up_done(), as it actually ignores + * all errors. */ + + mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); +} + +static void +set_highest_ms_class_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + guint new_class; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + new_class = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-class")); + if (new_class) + priv->current_ms_class = new_class; + + /* All done without errors! */ + mm_dbg ("[5/5] All done"); + enable_complete (MM_GENERIC_GSM (info->modem), NULL, info); +} + +static void +set_highest_ms_class (MMAtSerialPort *port, + MMCallbackInfo *info) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + guint new_class = 0; + + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) { + /* A is supported but is not currently selected, switch to A */ + new_class = WAVECOM_MS_CLASS_A; + } + } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_B) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_B) { + /* B is supported but is not currently selected, switch to B */ + new_class = WAVECOM_MS_CLASS_B; + } + } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_CG) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_CG) { + /* CG is supported but is not currently selected, switch to CG */ + new_class = WAVECOM_MS_CLASS_CG; + } + } + + if (new_class) { + const gchar *new_class_str; + gchar *cmd; + + new_class_str = wavecom_ms_class_to_str (new_class); + mm_dbg ("Changing mobile station class to: %s", new_class_str); + mm_callback_info_set_data (info, + "new-class", + GUINT_TO_POINTER (new_class), + NULL); + cmd = g_strdup_printf ("+CGCLASS=%s", new_class_str); + mm_at_serial_port_queue_command (port, cmd, 3, set_highest_ms_class_cb, info); + g_free (cmd); + return; + } + + /* if no need to change station class, then just go on */ + mm_dbg ("No need to change mobile station class"); + set_highest_ms_class_cb (port, NULL, NULL, info); +} + +static void +get_current_ms_class_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + const gchar *p; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + p = mm_strip_tag (response->str, "+CGCLASS:"); + + if (strncmp (p, + WAVECOM_MS_CLASS_A_IDSTR, + strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class A mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_A; + } else if (strncmp (p, + WAVECOM_MS_CLASS_B_IDSTR, + strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class B mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_B; + } else if (strncmp (p, + WAVECOM_MS_CLASS_CG_IDSTR, + strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class CG mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_CG; + } else if (strncmp (p, + WAVECOM_MS_CLASS_CC_IDSTR, + strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class CC mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_CC; + } else { + GError *inner_error; + + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown mobile station class: '%s'", + p); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + return; + } + + /* Next, set highest mobile station class possible */ + mm_dbg ("[4/5] Ensuring highest MS class..."); + set_highest_ms_class (port, info); +} + +static void +get_supported_ms_classes_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + const gchar *p; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + /* Reset currently supported MS classes */ + priv->supported_ms_classes = 0; + + p = mm_strip_tag (response->str, "+CGCLASS:"); + + if (strstr (p, WAVECOM_MS_CLASS_A_IDSTR)) { + mm_dbg ("Modem supports Class A mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_A; + } + + if (strstr (p, WAVECOM_MS_CLASS_B_IDSTR)) { + mm_dbg ("Modem supports Class B mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_B; + } + + if (strstr (p, WAVECOM_MS_CLASS_CG_IDSTR)) { + mm_dbg ("Modem supports Class CG mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_CG; + } + + if (strstr (p, WAVECOM_MS_CLASS_CC_IDSTR)) { + mm_dbg ("Modem supports Class CC mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_CC; + } + + /* If none received, error */ + if (!priv->supported_ms_classes) { + GError *inner_error; + + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get supported mobile station classes"); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + return; + } + + /* Next, query for current MS class */ + mm_dbg ("[3/5] Getting current MS class..."); + mm_at_serial_port_queue_command (port, "+CGCLASS?", 3, get_current_ms_class_cb, info); +} + +static void +get_current_functionality_status_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + const gchar *p; + GError *inner_error; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + p = mm_strip_tag (response->str, "+CFUN:"); + if (!p || *p != '1') { + /* Reported functionality status MUST be '1'. Otherwise, RF is probably + * switched off. */ + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unexpected functionality status: '%c'. ", + p ? *p :' '); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + } + + /* Nex, query for supported MS classes */ + mm_dbg ("[2/5] Getting supported MS classes..."); + mm_at_serial_port_queue_command (port, "+CGCLASS=?", 3, get_supported_ms_classes_cb, info); +} + +static void +do_enable_power_up_done (MMGenericGsm *gsm, + GString *response, + GError *error, + MMCallbackInfo *info) +{ + MMAtSerialPort *port; + GError *inner_error = NULL; + + if (error) { + enable_complete (gsm, error, info); + return; + } + + /* Get port */ + port = mm_generic_gsm_get_best_at_port (gsm, &inner_error); + if (!port) { + enable_complete (gsm, inner_error, info); + g_error_free (inner_error); + return; + } + + /* Next, get current functionality status */ + mm_dbg ("[1/5] Getting current functionality status..."); + mm_at_serial_port_queue_command (port, "+CFUN?", 3, get_current_functionality_status_cb, info); +} + +/*****************************************************************************/ + +static void +modem_init (MMModem *modem_class) +{ + modem_class->grab_port = grab_port; +} + +static void +modem_gsm_network_init (MMModemGsmNetwork *network_class) +{ + network_class->set_band = set_band; + network_class->get_band = get_band; +} + +static void +mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + + /* Set defaults */ + priv->supported_ms_classes = 0; /* This is a bitmask, so empty */ + priv->current_ms_class = WAVECOM_MS_CLASS_UNKNOWN; + priv->allowed_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; + priv->current_bands = 0; /* This is a bitmask, so empty */ +} + +static void +mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMModemWavecomGsmPrivate)); + + 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_FLOW_CONTROL_CMD, + MM_GENERIC_GSM_FLOW_CONTROL_CMD); + + gsm_class->do_enable_power_up_done = do_enable_power_up_done; + gsm_class->set_allowed_mode = set_allowed_mode; + gsm_class->get_allowed_mode = get_allowed_mode; + gsm_class->get_access_technology = get_access_technology; +} + diff --git a/plugins/mm-modem-wavecom-gsm.h b/plugins/mm-modem-wavecom-gsm.h new file mode 100644 index 0000000..f8289e3 --- /dev/null +++ b/plugins/mm-modem-wavecom-gsm.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) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#ifndef MM_MODEM_WAVECOM_GSM_H +#define MM_MODEM_WAVECOM_GSM_H + +#include "mm-generic-gsm.h" + +#define MM_TYPE_MODEM_WAVECOM_GSM (mm_modem_wavecom_gsm_get_type ()) +#define MM_MODEM_WAVECOM_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsm)) +#define MM_MODEM_WAVECOM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM, MMModemWavecomGsmClass)) +#define MM_IS_MODEM_WAVECOM_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM)) +#define MM_IS_MODEM_WAVECOM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM)) +#define MM_MODEM_WAVECOM_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_WAVECOM_AIRLINK_GSM, MMModemWavecomGsmClass)) + +typedef struct { + MMGenericGsm parent; +} MMModemWavecomGsm; + +typedef struct { + MMGenericGsmClass parent; +} MMModemWavecomGsmClass; + +GType mm_modem_wavecom_gsm_get_type (void); + +MMModem *mm_modem_wavecom_gsm_new (const char *device, + const char *driver, + const char *plugin_name, + guint32 vendor, + guint32 product); + +#endif /* MM_MODEM_WAVECOM_GSM_H */ diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index c447c58..e7387a0 100644 --- a/plugins/mm-modem-zte.c +++ b/plugins/mm-modem-zte.c @@ -39,10 +39,12 @@ G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, #define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate)) typedef struct { + gboolean disposed; gboolean init_retried; guint32 cpms_tries; guint cpms_timeout; gboolean is_icera; + MMModemIceraPrivate *icera; } MMModemZtePrivate; MMModem * @@ -52,17 +54,23 @@ mm_modem_zte_new (const char *device, guint32 vendor, guint32 product) { + MMModem *modem; + 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, - MM_MODEM_HW_VID, vendor, - MM_MODEM_HW_PID, product, - NULL)); + modem = MM_MODEM (g_object_new (MM_TYPE_MODEM_ZTE, + MM_MODEM_MASTER_DEVICE, device, + MM_MODEM_DRIVER, driver, + MM_MODEM_PLUGIN, plugin, + MM_MODEM_HW_VID, vendor, + MM_MODEM_HW_PID, product, + NULL)); + if (modem) + MM_MODEM_ZTE_GET_PRIVATE (modem)->icera = mm_modem_icera_init_private (); + + return modem; } /*****************************************************************************/ @@ -547,12 +555,13 @@ simple_connect (MMModemSimple *simple, MMModemFn callback, gpointer user_data) { - MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (simple); + MMModemZte *self = MM_MODEM_ZTE (simple); + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemSimple *parent_iface; if (priv->is_icera) - mm_modem_icera_simple_connect (MM_MODEM_ICERA (simple), properties); + mm_modem_icera_simple_connect (MM_MODEM_ICERA (self), properties); parent_iface = g_type_interface_peek_parent (MM_MODEM_SIMPLE_GET_INTERFACE (simple)); parent_iface->connect (MM_MODEM_SIMPLE (simple), properties, callback, info); @@ -619,6 +628,14 @@ grab_port (MMModem *modem, /*****************************************************************************/ +static MMModemIceraPrivate * +get_icera_private (MMModemIcera *icera) +{ + return MM_MODEM_ZTE_GET_PRIVATE (icera)->icera; +} + +/*****************************************************************************/ + static void modem_init (MMModem *modem_class) { @@ -629,9 +646,9 @@ modem_init (MMModem *modem_class) } static void -modem_icera_init (MMModemIcera *icera_class) +modem_icera_init (MMModemIcera *icera) { - mm_modem_icera_prepare (icera_class); + icera->get_private = get_icera_private; } static void @@ -651,10 +668,14 @@ dispose (GObject *object) MMModemZte *self = MM_MODEM_ZTE (object); MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (self); - if (priv->cpms_timeout) - g_source_remove (priv->cpms_timeout); + if (priv->disposed == FALSE) { + priv->disposed = TRUE; - mm_modem_icera_cleanup (MM_MODEM_ICERA (self)); + if (priv->cpms_timeout) + g_source_remove (priv->cpms_timeout); + + mm_modem_icera_dispose_private (MM_MODEM_ICERA (self)); + } G_OBJECT_CLASS (mm_modem_zte_parent_class)->dispose (object); } diff --git a/plugins/mm-plugin-anydata.c b/plugins/mm-plugin-anydata.c index 76df673..d4c7e6d 100644 --- a/plugins/mm-plugin-anydata.c +++ b/plugins/mm-plugin-anydata.c @@ -94,7 +94,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c index a27704b..89912c2 100644 --- a/plugins/mm-plugin-generic.c +++ b/plugins/mm-plugin-generic.c @@ -97,7 +97,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-gobi.c b/plugins/mm-plugin-gobi.c index 36771d1..06f5585 100644 --- a/plugins/mm-plugin-gobi.c +++ b/plugins/mm-plugin-gobi.c @@ -90,7 +90,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-hso.c b/plugins/mm-plugin-hso.c index 7b71932..a095802 100644 --- a/plugins/mm-plugin-hso.c +++ b/plugins/mm-plugin-hso.c @@ -88,7 +88,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 0, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c index f1590ca..32edfde 100644 --- a/plugins/mm-plugin-huawei.c +++ b/plugins/mm-plugin-huawei.c @@ -15,6 +15,7 @@ */ #include +#include #include #define G_UDEV_API_IS_SUBJECT_TO_CHANGE @@ -28,6 +29,7 @@ #include "mm-serial-parsers.h" #include "mm-at-serial-port.h" #include "mm-log.h" +#include "mm-errors.h" G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN_BASE) @@ -44,6 +46,8 @@ mm_plugin_create (void) /*****************************************************************************/ +#define TAG_HUAWEI_PCUI_PORT "huawei-pcui-port" + #define CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ MM_PLUGIN_BASE_PORT_CAP_IS856 | \ @@ -70,95 +74,59 @@ probe_result (MMPluginBase *base, mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); } -#define TAG_SUPPORTS_INFO "huawei-supports-info" - -typedef struct { - MMAtSerialPort *serial; - guint id; - MMPortType ptype; - /* Whether or not there's already a detected modem that "owns" this port, - * in which case we'll claim it, but if no capabilities are detected it'll - * just be ignored. - */ - gboolean parent_modem; -} HuaweiSupportsInfo; - -static void -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) +getportmode_response_cb (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + 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; + /* If any error occurred that was not ERROR or COMMAND NOT SUPPORT then + * retry the command. + */ + if (error) { + if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_UNKNOWN) == FALSE) + return tries <= 4 ? TRUE : FALSE; + } else { + MMPlugin *plugin; + char *p; + int i = 0; + + /* Get the USB interface number of the PCUI port */ + p = strstr (response->str, "PCUI:"); + if (p) + i = atoi (p + strlen ("PCUI:")); + + if (i) { + /* Save they PCUI port number for later */ + plugin = mm_plugin_base_supports_task_get_plugin (task); + g_assert (plugin); + g_object_set_data (G_OBJECT (plugin), TAG_HUAWEI_PCUI_PORT, GINT_TO_POINTER (i)); + } + } - /* Yay, supported, we got an unsolicited message */ - info->ptype = MM_PORT_TYPE_SECONDARY; - mm_plugin_base_supports_task_complete (task, 10); + /* No error or if ^GETPORTMODE is not supported, assume success */ return FALSE; } -static void -probe_secondary_handle_msg (MMAtSerialPort *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) +curc_response_cb (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data) { - MMPluginBaseSupportsTask *task = user_data; - HuaweiSupportsInfo *info; - guint level = 0; - - info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - info->id = 0; - g_object_unref (info->serial); - info->serial = NULL; - - /* Supported, but ignored if this port's parent device is already a modem */ - if (info->parent_modem) { - info->ptype = MM_PORT_TYPE_IGNORED; - level = 10; - } + if (error) + return tries <= 4 ? TRUE : FALSE; - mm_plugin_base_supports_task_complete (task, level); + /* No error, assume success */ return FALSE; } -static void -add_regex (MMAtSerialPort *port, const char *match, gpointer user_data) -{ - GRegex *regex; - - regex = g_regex_new (match, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_at_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, @@ -169,7 +137,6 @@ supports_port (MMPluginBase *base, 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); @@ -189,72 +156,48 @@ supports_port (MMPluginBase *base, 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. + /* The primary port (called the "modem" port in the Windows drivers) is + * always USB interface 0, and we need to detect that interface first for + * two reasons: (1) to disable unsolicited messages on other ports that + * may fill up the buffer and crash the device, and (2) to attempt to get + * the port layout for hints about what the secondary port is (called the + * "pcui" port in Windows). Thus we probe USB interface 0 first and defer + * probing other interfaces until we've got if0, at which point we allow + * the other ports to be probed too. */ 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)) + 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; - } else { - HuaweiSupportsInfo *info; - GError *error = NULL; - - /* Listen for Huawei-specific unsolicited messages */ - info = g_malloc0 (sizeof (HuaweiSupportsInfo)); - info->parent_modem = !!existing; - - info->serial = mm_at_serial_port_new (name, MM_PORT_TYPE_PRIMARY); - g_object_set (G_OBJECT (info->serial), MM_PORT_CARRIER_DETECT, FALSE, NULL); - - mm_at_serial_port_set_response_parser (info->serial, - mm_serial_parser_v1_parse, - mm_serial_parser_v1_new (), - mm_serial_parser_v1_destroy); - - add_regex (info->serial, "\\r\\n\\^RSSI:(\\d+)\\r\\n", task); - add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task); - 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_seconds (7, probe_secondary_timeout, task); - - if (!mm_serial_port_open (MM_SERIAL_PORT (info->serial), &error)) { - mm_warn ("(Huawei) %s: couldn't open serial port: (%d) %s", - name, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - huawei_supports_info_destroy (info); - return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + } - g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO, - info, huawei_supports_info_destroy); + /* Turn off unsolicited messages on secondary ports until needed, + * and try to get a port map from the modem. The response will + * get handled in custom_init_response(). + */ + if (usbif == 0) { + mm_plugin_base_supports_task_add_custom_init_command (task, + "AT^CURC=0", + 3, /* delay */ + curc_response_cb, + NULL); + + mm_plugin_base_supports_task_add_custom_init_command (task, + "AT^GETPORTMODE", + 3, /* delay */ + getportmode_response_cb, + NULL); + } + /* Kick off a probe */ + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; - } return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } @@ -314,12 +257,21 @@ grab_port (MMPluginBase *base, } } } else { - HuaweiSupportsInfo *info; MMPortType ptype = MM_PORT_TYPE_UNKNOWN; + int pcui_usbif, port_usbif; + + /* Any additional AT ports can be secondary ports, but we want to ensure + * that the "pcui" port found from ^GETPORTMODE above is always set as + * a secondary port too. + */ + + port_usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM"); + pcui_usbif = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (base), TAG_HUAWEI_PCUI_PORT)); - info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO); - if (info) - ptype = info->ptype; + if ( (port_usbif == pcui_usbif) + || (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) + || (caps & CAP_CDMA)) + ptype = MM_PORT_TYPE_SECONDARY; else if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM) ptype = MM_PORT_TYPE_QCDM; diff --git a/plugins/mm-plugin-linktop.c b/plugins/mm-plugin-linktop.c index 411f9cc..8d2e199 100644 --- a/plugins/mm-plugin-linktop.c +++ b/plugins/mm-plugin-linktop.c @@ -85,7 +85,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-longcheer.c b/plugins/mm-plugin-longcheer.c index e63ff4e..cbf4c1e 100644 --- a/plugins/mm-plugin-longcheer.c +++ b/plugins/mm-plugin-longcheer.c @@ -97,7 +97,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-mbm.c b/plugins/mm-plugin-mbm.c index 6684ff9..d770ad4 100644 --- a/plugins/mm-plugin-mbm.c +++ b/plugins/mm-plugin-mbm.c @@ -116,7 +116,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 0, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-moto-c.c b/plugins/mm-plugin-moto-c.c index 21b737e..f308b9d 100644 --- a/plugins/mm-plugin-moto-c.c +++ b/plugins/mm-plugin-moto-c.c @@ -90,7 +90,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-nokia.c b/plugins/mm-plugin-nokia.c index 938f8c5..28e9022 100644 --- a/plugins/mm-plugin-nokia.c +++ b/plugins/mm-plugin-nokia.c @@ -93,7 +93,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-novatel.c b/plugins/mm-plugin-novatel.c index 5384456..d960add 100644 --- a/plugins/mm-plugin-novatel.c +++ b/plugins/mm-plugin-novatel.c @@ -99,7 +99,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-option.c b/plugins/mm-plugin-option.c index ab2bd3d..a819b4e 100644 --- a/plugins/mm-plugin-option.c +++ b/plugins/mm-plugin-option.c @@ -89,7 +89,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 0, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-samsung.c b/plugins/mm-plugin-samsung.c new file mode 100755 index 0000000..eb5e071 --- /dev/null +++ b/plugins/mm-plugin-samsung.c @@ -0,0 +1,157 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2011 Red Hat, Inc. + * Copyright (c) 2011 Samsung Electronics, Inc., + */ + +#include +#include +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include + +#include "mm-plugin-samsung.h" +#include "mm-modem-samsung-gsm.h" + +G_DEFINE_TYPE (MMPluginSamsung, mm_plugin_samsung, 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_SAMSUNG, + MM_PLUGIN_BASE_NAME, "Samsung", + 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; + 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; + const char *subsys, *name; + guint16 vendor = 0, product = 0; + + port = mm_plugin_base_supports_task_get_port (task); + + subsys = g_udev_device_get_subsystem (port); + g_assert (subsys); + name = g_udev_device_get_name (port); + g_assert (name); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* Vendor ID check */ + if (vendor != 0x04e8 && vendor != 0x1983) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* The ethernet ports are obviously supported and don't need probing */ + if (!strcmp (subsys, "net")) { + mm_plugin_base_supports_task_complete (task, 10); + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + } + + /* Otherwise kick off a probe */ + if (mm_plugin_base_probe_port (base, task, 0, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL; + MMModem *modem = NULL; + guint32 caps; + const char *name, *subsys, *sysfs_path; + + 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); + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + if (caps & CAP_CDMA) { + g_set_error (error, 0, 0, "Only GSM modems are currently supported by this plugin."); + return NULL; + } + + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + modem = mm_modem_samsung_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_samsung_init (MMPluginSamsung *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_samsung_class_init (MMPluginSamsungClass *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-samsung.h b/plugins/mm-plugin-samsung.h new file mode 100755 index 0000000..e79daa2 --- /dev/null +++ b/plugins/mm-plugin-samsung.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. + * Copyright 2011 by Samsung Electronics, Inc., + */ + +#ifndef MM_PLUGIN_SAMSUNG_H +#define MM_PLUGIN_SAMSUNG_H + +#include "mm-plugin.h" +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_SAMSUNG (mm_plugin_samsung_get_type ()) +#define MM_PLUGIN_SAMSUNG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SAMSUNG, MMPluginSamsung)) +#define MM_PLUGIN_SAMSUNG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SAMSUNG, MMPluginSamsungClass)) +#define MM_IS_PLUGIN_SAMSUNG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SAMSUNG)) +#define MM_IS_PLUGIN_SAMSUNG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SAMSUNG)) +#define MM_PLUGIN_SAMSUNG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SAMSUNG, MMPluginSamsungClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginSamsung; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginSamsungClass; + +GType mm_plugin_samsung_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_SAMSUNG_H */ + diff --git a/plugins/mm-plugin-sierra.c b/plugins/mm-plugin-sierra.c index b7367ab..408074b 100644 --- a/plugins/mm-plugin-sierra.c +++ b/plugins/mm-plugin-sierra.c @@ -122,7 +122,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-simtech.c b/plugins/mm-plugin-simtech.c index 611d2d2..7e2e634 100644 --- a/plugins/mm-plugin-simtech.c +++ b/plugins/mm-plugin-simtech.c @@ -97,7 +97,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-wavecom.c b/plugins/mm-plugin-wavecom.c new file mode 100644 index 0000000..c7e3f4a --- /dev/null +++ b/plugins/mm-plugin-wavecom.c @@ -0,0 +1,162 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#include +#include +#include "mm-plugin-wavecom.h" +#include "mm-modem-wavecom-gsm.h" + +G_DEFINE_TYPE (MMPluginWavecom, mm_plugin_wavecom, 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_WAVECOM, + MM_PLUGIN_BASE_NAME, "Wavecom", + NULL)); +} + +static guint32 +get_level_for_capabilities (guint32 capabilities) +{ + if (capabilities & MM_PLUGIN_BASE_PORT_CAP_GSM) + return 10; + return 0; +} + +static void +probe_result (MMPluginBase *base, + MMPluginBaseSupportsTask *task, + guint32 capabilities, + gpointer user_data) +{ + mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); +} + +static MMPluginSupportsResult +supports_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task) +{ + GUdevDevice *port; + guint32 cached = 0, level; + const char *subsys, *name; + guint16 vendor = 0; + + /* Can't do anything with non-serial ports */ + port = mm_plugin_base_supports_task_get_port (task); + if (strcmp (g_udev_device_get_subsystem (port), "tty")) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + + /* Vendor: Wavecom */ + if (vendor != 0x114f) + 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, 100000, NULL)) + return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; + + return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; +} + +static MMModem * +grab_port (MMPluginBase *base, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error) +{ + GUdevDevice *port = NULL; + MMModem *modem = NULL; + const char *name, *subsys, *sysfs_path; + guint32 caps; + guint16 vendor = 0, product = 0; + + 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); + + if (!mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product)) { + g_set_error (error, 0, 0, "Could not get modem product ID."); + return NULL; + } + + caps = mm_plugin_base_supports_task_get_probed_capabilities (task); + sysfs_path = mm_plugin_base_supports_task_get_physdev_path (task); + if (!existing) { + if (caps & MM_PLUGIN_BASE_PORT_CAP_GSM) { + modem = mm_modem_wavecom_gsm_new (sysfs_path, + mm_plugin_base_supports_task_get_driver (task), + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product); + } + + if (modem) { + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) { + g_object_unref (modem); + return NULL; + } + } + } else if (get_level_for_capabilities (caps)) { + modem = existing; + if (!mm_modem_grab_port (modem, subsys, name, MM_PORT_TYPE_UNKNOWN, NULL, error)) + return NULL; + } + + return modem; +} + +/*****************************************************************************/ + +static void +mm_plugin_wavecom_init (MMPluginWavecom *self) +{ + g_signal_connect (self, "probe-result", G_CALLBACK (probe_result), NULL); +} + +static void +mm_plugin_wavecom_class_init (MMPluginWavecomClass *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-wavecom.h b/plugins/mm-plugin-wavecom.h new file mode 100644 index 0000000..d01bf15 --- /dev/null +++ b/plugins/mm-plugin-wavecom.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2011 Ammonit Gesellschaft für Messtechnik mbH + */ + +#ifndef MM_PLUGIN_WAVECOM_H +#define MM_PLUGIN_WAVECOM_H + +#include "mm-plugin-base.h" +#include "mm-generic-gsm.h" + +#define MM_TYPE_PLUGIN_WAVECOM (mm_plugin_wavecom_get_type ()) +#define MM_PLUGIN_WAVECOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecom)) +#define MM_PLUGIN_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecomClass)) +#define MM_IS_PLUGIN_WAVECOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_WAVECOM)) +#define MM_IS_PLUGIN_WAVECOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_WAVECOM)) +#define MM_PLUGIN_WAVECOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_WAVECOM, MMPluginWavecomClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginWavecom; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginWavecomClass; + +GType mm_plugin_wavecom_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_WAVECOM_H */ diff --git a/plugins/mm-plugin-x22x.c b/plugins/mm-plugin-x22x.c index 5a8253f..f99b9d6 100644 --- a/plugins/mm-plugin-x22x.c +++ b/plugins/mm-plugin-x22x.c @@ -98,7 +98,7 @@ supports_port (MMPluginBase *base, } /* Otherwise kick off a probe */ - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; diff --git a/plugins/mm-plugin-zte.c b/plugins/mm-plugin-zte.c index bb2ee17..37033e0 100644 --- a/plugins/mm-plugin-zte.c +++ b/plugins/mm-plugin-zte.c @@ -61,6 +61,22 @@ probe_result (MMPluginBase *base, mm_plugin_base_supports_task_complete (task, get_level_for_capabilities (capabilities)); } +static gboolean +custom_init_response_cb (MMPluginBaseSupportsTask *task, + GString *response, + GError *error, + guint32 tries, + gboolean *out_fail, + guint32 *out_level, + gpointer user_data) +{ + if (error) + return tries <= 4 ? TRUE : FALSE; + + /* No error, assume success */ + return FALSE; +} + static MMPluginSupportsResult supports_port (MMPluginBase *base, MMModem *existing, @@ -113,9 +129,13 @@ supports_port (MMPluginBase *base, * 1235f71b20c92cded4abd976ccc5010649aae1a0 and * f38ad328acfdc6ce29dd1380602c546b064161ae for more details. */ - mm_plugin_base_supports_task_set_custom_init_command (task, "ATE0+CPMS?", 3, 4, FALSE); + mm_plugin_base_supports_task_add_custom_init_command (task, + "ATE0+CPMS?", + 3, + custom_init_response_cb, + NULL); - if (mm_plugin_base_probe_port (base, task, NULL)) + if (mm_plugin_base_probe_port (base, task, 100000, NULL)) return MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS; return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; -- cgit v1.2.3