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.c230
1 files changed, 91 insertions, 139 deletions
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 <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, 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;