aboutsummaryrefslogtreecommitdiff
path: root/plugins/mm-plugin-huawei.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mm-plugin-huawei.c')
-rw-r--r--plugins/mm-plugin-huawei.c237
1 files changed, 145 insertions, 92 deletions
diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c
index aef3c52..fe7ffa0 100644
--- a/plugins/mm-plugin-huawei.c
+++ b/plugins/mm-plugin-huawei.c
@@ -15,7 +15,6 @@
*/
#include <string.h>
-#include <stdlib.h>
#include <gmodule.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
@@ -29,7 +28,6 @@
#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)
@@ -46,8 +44,6 @@ 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 | \
@@ -74,59 +70,95 @@ 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
-getportmode_response_cb (MMPluginBaseSupportsTask *task,
- GString *response,
- GError *error,
- guint32 tries,
- gboolean *out_stop,
- guint32 *out_level,
- gpointer user_data)
+probe_secondary_supported (gpointer user_data)
{
- /* 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));
- }
- }
+ MMPluginBaseSupportsTask *task = user_data;
+ HuaweiSupportsInfo *info;
+
+ info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO);
- /* No error or if ^GETPORTMODE is not supported, assume success */
+ info->id = 0;
+ g_object_unref (info->serial);
+ info->serial = NULL;
+
+ /* Yay, supported, we got an unsolicited message */
+ info->ptype = MM_PORT_TYPE_SECONDARY;
+ mm_plugin_base_supports_task_complete (task, 10);
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
-curc_response_cb (MMPluginBaseSupportsTask *task,
- GString *response,
- GError *error,
- guint32 tries,
- gboolean *out_stop,
- guint32 *out_level,
- gpointer user_data)
+probe_secondary_timeout (gpointer user_data)
{
- if (error)
- return tries <= 4 ? TRUE : FALSE;
+ 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;
+ }
- /* No error, assume success */
+ mm_plugin_base_supports_task_complete (task, level);
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,
@@ -134,9 +166,10 @@ supports_port (MMPluginBase *base,
{
GUdevDevice *port;
guint32 cached = 0, level;
- const char *subsys, *name;
+ const char *subsys, *name, *driver;
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);
@@ -152,52 +185,81 @@ supports_port (MMPluginBase *base,
if (vendor != 0x12d1)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+ /* The Gobi driver should claim Huawei Gobi modems */
+ driver = mm_plugin_base_supports_task_get_driver (task);
+ if (g_strcmp0 (driver, "qcserial") == 0)
+ return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+
usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM");
if (usbif < 0)
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- /* The 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.
+ /* The secondary ports don't necessarily respond correctly to probing, so
+ * we need to use the first port that does respond to probing to create the
+ * right type of mode (GSM or CDMA), and then re-check the other interfaces.
*/
if (!existing && usbif != 0)
return MM_PLUGIN_SUPPORTS_PORT_DEFER;
- 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);
+ /* 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))
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;
- }
- /* 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);
- }
+ g_object_set_data_full (G_OBJECT (task), TAG_SUPPORTS_INFO,
+ info, huawei_supports_info_destroy);
- /* 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;
}
@@ -257,21 +319,12 @@ 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));
- if ( (port_usbif == pcui_usbif)
- || (caps & MM_PLUGIN_BASE_PORT_CAP_GSM)
- || (caps & CAP_CDMA))
- ptype = MM_PORT_TYPE_SECONDARY;
+ info = g_object_get_data (G_OBJECT (task), TAG_SUPPORTS_INFO);
+ if (info)
+ ptype = info->ptype;
else if (caps & MM_PLUGIN_BASE_PORT_CAP_QCDM)
ptype = MM_PORT_TYPE_QCDM;