aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2011-04-27 10:50:32 -0500
committerDan Williams <dcbw@redhat.com>2011-04-27 10:50:32 -0500
commitdc89c0a42d826fc3302b3d790d5161945ff7078f (patch)
tree6179ec77b728177354330644e67e6bd9277655fe
parent1cf7a4da4495fdd1d237b04bc35732a93f42fdf1 (diff)
huawei: rework probing and detection
Long ago there were problems where certain Huawei devices would stop responding on various ports, and sometimes would crash randomly. The theory at the time was that touching the secondary ports made the device angry, thus the plugin simply opened the ports and listened for unsolicited messages. But if the device didn't send any during that 7 second period, MM would not detect and secondary ports at all. Plus, it was always a hack. Instead, the new theory is that the device crashes if unsolicited messages are enabled (^CURC=1), the secondary port gets touched, *and* then closed and left for a while. Fix that by turning unsolicited messages off at probe time, on when the device is enabled, and off again when the device is disabled like happens for other modems. Thus when MM first detects the modem, it turns off unsolicited messages and the serial buffer on the secondary port doesn't fill up and crash the modem. Second, this allows us to simplify the probing logic quite a bit so that we can probe all ports we find, but we still wait to probe the first port so we can turn off unsolicited messages and get hints about what port is the secondary.
-rw-r--r--plugins/mm-modem-huawei-gsm.c67
-rw-r--r--plugins/mm-plugin-huawei.c230
2 files changed, 158 insertions, 139 deletions
diff --git a/plugins/mm-modem-huawei-gsm.c b/plugins/mm-modem-huawei-gsm.c
index 3fc9ae26..c9a8a3f8 100644
--- a/plugins/mm-modem-huawei-gsm.c
+++ b/plugins/mm-modem-huawei-gsm.c
@@ -687,6 +687,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 +831,7 @@ static void
modem_init (MMModem *modem_class)
{
modem_class->grab_port = grab_port;
+ modem_class->disable = disable;
}
static void
@@ -798,5 +864,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-plugin-huawei.c b/plugins/mm-plugin-huawei.c
index 48697bbd..32edfde7 100644
--- a/plugins/mm-plugin-huawei.c
+++ b/plugins/mm-plugin-huawei.c
@@ -15,6 +15,7 @@
*/
#include <string.h>
+#include <stdlib.h>
#include <gmodule.h>
#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, 100000, 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;