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