diff options
Diffstat (limited to 'plugins/mm-plugin-huawei.c')
-rw-r--r-- | plugins/mm-plugin-huawei.c | 230 |
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; |