From f91ee7af7d6a38eda226afb429b26ffe1408e08f Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 30 Mar 2011 17:22:53 +0200 Subject: [PATCH 01/10] wavecom: add initial dummy plugin --- plugins/Makefile.am | 21 ++++- plugins/mm-modem-wavecom-gsm.c | 74 +++++++++++++++ plugins/mm-modem-wavecom-gsm.h | 44 +++++++++ plugins/mm-plugin-wavecom.c | 162 +++++++++++++++++++++++++++++++++ plugins/mm-plugin-wavecom.h | 47 ++++++++++ 5 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 plugins/mm-modem-wavecom-gsm.c create mode 100644 plugins/mm-modem-wavecom-gsm.h create mode 100644 plugins/mm-plugin-wavecom.c create mode 100644 plugins/mm-plugin-wavecom.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 7c1811b2..8eaffb42 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -36,7 +36,8 @@ pkglib_LTLIBRARIES = \ libmm-plugin-simtech.la \ libmm-plugin-x22x.la \ libmm-plugin-linktop.la \ - libmm-plugin-samsung.la + libmm-plugin-samsung.la \ + libmm-plugin-wavecom.la # Generic @@ -188,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 = \ diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c new file mode 100644 index 00000000..b28df078 --- /dev/null +++ b/plugins/mm-modem-wavecom-gsm.c @@ -0,0 +1,74 @@ +/* -*- 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-modem-wavecom-gsm.h" +#include "mm-errors.h" +#include "mm-modem-simple.h" +#include "mm-callback-info.h" +#include "mm-modem-helpers.h" +#include "mm-at-serial-port.h" + +static void modem_init (MMModem *modem_class); + +G_DEFINE_TYPE_EXTENDED (MMModemWavecomGsm, + mm_modem_wavecom_gsm, + MM_TYPE_GENERIC_GSM, + 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) + +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 void +modem_init (MMModem *modem_class) +{ +} + +static void +mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self) +{ +} + +static void +mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) +{ +} + diff --git a/plugins/mm-modem-wavecom-gsm.h b/plugins/mm-modem-wavecom-gsm.h new file mode 100644 index 00000000..f8289e3b --- /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-plugin-wavecom.c b/plugins/mm-plugin-wavecom.c new file mode 100644 index 00000000..c7e3f4ab --- /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 00000000..d01bf156 --- /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 */ From 262edb96d50138a724173840fc63ca93ede322c2 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 30 Mar 2011 18:52:23 +0200 Subject: [PATCH 02/10] wavecom: handle READY without OK in CPIN? reply --- plugins/mm-modem-wavecom-gsm.c | 51 ++++++++++++++++++++++++++++++---- src/mm-serial-parsers.c | 3 +- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index b28df078..3e39ffff 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -21,11 +21,7 @@ #include #include "mm-modem-wavecom-gsm.h" -#include "mm-errors.h" -#include "mm-modem-simple.h" -#include "mm-callback-info.h" -#include "mm-modem-helpers.h" -#include "mm-at-serial-port.h" +#include "mm-serial-parsers.h" static void modem_init (MMModem *modem_class); @@ -55,11 +51,56 @@ mm_modem_wavecom_gsm_new (const char *device, 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 modem_init (MMModem *modem_class) { + modem_class->grab_port = grab_port; } static void diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index f6798314..c1d010aa 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -270,10 +270,11 @@ mm_serial_parser_v1_parse (gpointer data, /* First, check for successful responses */ /* Custom successful replies first, if any */ - if (parser->regex_custom_successful) + if (parser->regex_custom_successful) { found = g_regex_match_full (parser->regex_custom_successful, response->str, response->len, 0, 0, NULL, NULL); + } if (!found) { found = g_regex_match_full (parser->regex_ok, From 1fb0b9d79b9fd2d2004653a316b2bbbcc0efcc1f Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Thu, 31 Mar 2011 09:23:29 +0200 Subject: [PATCH 03/10] wavecom: disable default power up command --- plugins/mm-modem-wavecom-gsm.c | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index 3e39ffff..b080ac7f 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -95,6 +95,33 @@ grab_port (MMModem *modem, 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; + default: + break; + } +} + /*****************************************************************************/ static void @@ -111,5 +138,14 @@ mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self) static void mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_POWER_UP_CMD, + MM_GENERIC_GSM_POWER_UP_CMD); + } From 23649549bb5a6f4848656bd1ea3423401f771383 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Thu, 31 Mar 2011 10:45:09 +0200 Subject: [PATCH 04/10] wavecom: enable RTS/CTS flow control instead of XOFF/XON --- plugins/mm-modem-wavecom-gsm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index b080ac7f..8f4c215b 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -117,6 +117,10 @@ get_property (GObject *object, * 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; } @@ -147,5 +151,8 @@ mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) 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); } From acf65de3b844cd13c2128275daca67ac630146bb Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Mon, 4 Apr 2011 13:09:14 +0200 Subject: [PATCH 05/10] wavecom: query network technology capabilities --- plugins/mm-modem-wavecom-gsm.c | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index 8f4c215b..e68af89d 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -20,6 +20,8 @@ #include #include +#include "mm-errors.h" +#include "mm-modem-helpers.h" #include "mm-modem-wavecom-gsm.h" #include "mm-serial-parsers.h" @@ -126,6 +128,76 @@ get_property (GObject *object, } } +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 @@ -143,6 +215,7 @@ 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); object_class->get_property = get_property; object_class->set_property = set_property; @@ -154,5 +227,7 @@ mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) g_object_class_override_property (object_class, MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD, MM_GENERIC_GSM_FLOW_CONTROL_CMD); + + gsm_class->get_access_technology = get_access_technology; } From c7119f0341670cc258292b5d39a9082485018fbf Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 6 Apr 2011 11:21:45 +0200 Subject: [PATCH 06/10] wavecom: ensure the modem uses the highest possible mobile class These modems can be configured to use different mobile classes. For each kind of modem, the best mobile class is the highest one in the following order: - Class A (3G only mode) - Class B (PS or CS, GPRS/EDGE or GSM) - Class CG (PS only, GPRS/EDGE) - Class CC (CS only, GSM) --- plugins/mm-modem-wavecom-gsm.c | 273 +++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index e68af89d..480afbe3 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -24,6 +24,7 @@ #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); @@ -33,6 +34,35 @@ G_DEFINE_TYPE_EXTENDED (MMModemWavecomGsm, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_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\"" + +#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; +} MMModemWavecomGsmPrivate; + MMModem * mm_modem_wavecom_gsm_new (const char *device, const char *driver, @@ -53,6 +83,23 @@ mm_modem_wavecom_gsm_new (const char *device, 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, @@ -198,6 +245,224 @@ get_access_technology (MMGenericGsm *gsm, mm_at_serial_port_queue_command (port, "+WGPRS=9,2", 3, get_access_technology_cb, 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! */ + mm_dbg ("[4/4] 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; + } + + /* 3rd, set highest mobile station class possible */ + mm_dbg ("[3/4] 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; + } + + /* 2nd, query for current MS class */ + mm_dbg ("[2/4] Getting current MS class..."); + mm_at_serial_port_queue_command (port, "+CGCLASS?", 3, get_current_ms_class_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; + } + + /* 1st, query for supported MS classes */ + mm_dbg ("[1/4] Getting supported MS classes..."); + mm_at_serial_port_queue_command (port, "+CGCLASS=?", 3, get_supported_ms_classes_cb, info); +} + /*****************************************************************************/ static void @@ -209,6 +474,11 @@ modem_init (MMModem *modem_class) 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; } static void @@ -217,6 +487,8 @@ 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; @@ -228,6 +500,7 @@ mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) 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->get_access_technology = get_access_technology; } From e93f01f2017ad7e05c90d6ae18eaab49bbc17e7e Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 6 Apr 2011 11:31:40 +0200 Subject: [PATCH 07/10] wavecom: set and get allowed mode For 3G devices in Class A, AT+WWSM can be used to get or set the allowed mode: * +WWSM: 0 (2G only) * +WWSM: 1 (3G only) * +WWSM: 2,0 (Any) * +WWSM: 2,1 (2G preferred) * +WWSM: 2,2 (3G preferred) For 2G devices, there is no such command, so we will default to Any and allow 2G-only and 2G-preferred setups. --- plugins/mm-modem-wavecom-gsm.c | 224 +++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index 480afbe3..b76d81d9 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -61,6 +61,8 @@ typedef struct { guint8 supported_ms_classes; /* Current MS class */ WavecomMSClass current_ms_class; + /* Current allowed mode, only for 3G devices */ + MMModemGsmAllowedMode allowed_mode; } MMModemWavecomGsmPrivate; MMModem * @@ -245,6 +247,225 @@ get_access_technology (MMGenericGsm *gsm, 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, @@ -479,6 +700,7 @@ mm_modem_wavecom_gsm_init (MMModemWavecomGsm *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; } static void @@ -501,6 +723,8 @@ mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) 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; } From d1453b0e199588db20f8fef92922bdccfed46c66 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 6 Apr 2011 13:36:10 +0200 Subject: [PATCH 08/10] wavecom: ensure full functionality status Check the current functionality status with AT+CFUN? and make sure its '1', otherwise, RF may be switched off. --- plugins/mm-modem-wavecom-gsm.c | 54 +++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index b76d81d9..0156a88f 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -496,8 +496,8 @@ set_highest_ms_class_cb (MMAtSerialPort *port, if (new_class) priv->current_ms_class = new_class; - /* All done! */ - mm_dbg ("[4/4] All done..."); + /* All done without errors! */ + mm_dbg ("[5/5] All done"); enable_complete (MM_GENERIC_GSM (info->modem), NULL, info); } @@ -530,7 +530,7 @@ set_highest_ms_class (MMAtSerialPort *port, gchar *cmd; new_class_str = wavecom_ms_class_to_str (new_class); - mm_dbg (" Changing mobile station class to: %s", new_class_str); + mm_dbg ("Changing mobile station class to: %s", new_class_str); mm_callback_info_set_data (info, "new-class", GUINT_TO_POINTER (new_class), @@ -542,7 +542,7 @@ set_highest_ms_class (MMAtSerialPort *port, } /* if no need to change station class, then just go on */ - mm_dbg (" No need to change mobile station class"); + mm_dbg ("No need to change mobile station class"); set_highest_ms_class_cb (port, NULL, NULL, info); } @@ -595,8 +595,8 @@ get_current_ms_class_cb (MMAtSerialPort *port, return; } - /* 3rd, set highest mobile station class possible */ - mm_dbg ("[3/4] Ensuring highest MS class..."); + /* Next, set highest mobile station class possible */ + mm_dbg ("[4/5] Ensuring highest MS class..."); set_highest_ms_class (port, info); } @@ -652,11 +652,43 @@ get_supported_ms_classes_cb (MMAtSerialPort *port, return; } - /* 2nd, query for current MS class */ - mm_dbg ("[2/4] Getting current MS class..."); + /* 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, @@ -679,9 +711,9 @@ do_enable_power_up_done (MMGenericGsm *gsm, return; } - /* 1st, query for supported MS classes */ - mm_dbg ("[1/4] Getting supported MS classes..."); - mm_at_serial_port_queue_command (port, "+CGCLASS=?", 3, get_supported_ms_classes_cb, info); + /* 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); } /*****************************************************************************/ From ade8c17f6a37d83cfa7cee5c1a6a370522a936ca Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 6 Apr 2011 19:40:53 +0200 Subject: [PATCH 09/10] gsm: handle UMTS 2600 internal band --- src/mm-modem-gsm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h index a427d354..83dfb319 100644 --- a/src/mm-modem-gsm.h +++ b/src/mm-modem-gsm.h @@ -74,8 +74,9 @@ typedef enum { MM_MODEM_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */ MM_MODEM_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */ MM_MODEM_GSM_BAND_U1900 = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz (Class II) */ + MM_MODEM_GSM_BAND_U2600 = 0x00002000, /* WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) */ - MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U1900 + MM_MODEM_GSM_BAND_LAST = MM_MODEM_GSM_BAND_U2600 } MMModemGsmBand; From 765da63ad2ddb47d90ad8ffa5b2d0569301b2ba3 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 6 Apr 2011 19:41:19 +0200 Subject: [PATCH 10/10] wavecom: set and get bands Use AT+WMBS for devices in 2G mode, and AT+WUBS for devices in 3G mode. --- plugins/mm-modem-wavecom-gsm.c | 339 ++++++++++++++++++++++++++++++++- 1 file changed, 334 insertions(+), 5 deletions(-) diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index 0156a88f..362fec23 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -27,12 +27,11 @@ #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_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 { @@ -53,6 +52,25 @@ typedef enum { #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)) @@ -63,8 +81,45 @@ typedef struct { 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, @@ -177,6 +232,272 @@ get_property (GObject *object, } } +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; + } + + /* 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; + } + + /* 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, @@ -724,6 +1045,13 @@ 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) { @@ -733,6 +1061,7 @@ mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self) 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