diff options
author | Dan Williams <dcbw@redhat.com> | 2012-11-27 09:53:29 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2012-11-27 09:56:25 -0600 |
commit | c90e41fb1327ad48166b58a8f8281ac6462a9690 (patch) | |
tree | 21744b9d0409dbe19028e8361c59aacbb1f4918b | |
parent | a01e8aa31724ee1210b86d1d7261897a305d8710 (diff) |
port-probe: early-exit if port is certainly not AT capable
If we read a response that indicates the port is definitely not an
AT capable port, stop AT probing. Certain ports that use proprietary
protocols or other non-AT protocols tend to spew data at us, so when
this happens we can cut probing short.
-rw-r--r-- | src/mm-port-probe.c | 131 |
1 files changed, 74 insertions, 57 deletions
diff --git a/src/mm-port-probe.c b/src/mm-port-probe.c index 598ceb6e..263b6c42 100644 --- a/src/mm-port-probe.c +++ b/src/mm-port-probe.c @@ -347,12 +347,15 @@ port_probe_run_is_cancelled (MMPortProbe *self) return TRUE; } return FALSE; } +/***************************************************************/ +/* QMI */ + #if defined WITH_QMI static void qmi_port_open_ready (MMQmiPort *qmi_port, GAsyncResult *res, MMPortProbe *self) @@ -408,12 +411,15 @@ wdm_probe_qmi (MMPortProbe *self) port_probe_run_task_complete (task, TRUE, NULL); #endif /* WITH_QMI */ return FALSE; } +/***************************************************************/ +/* QCDM */ + static void serial_probe_qcdm_parse_response (MMQcdmSerialPort *port, GByteArray *response, GError *error, MMPortProbe *self) { @@ -564,12 +570,65 @@ serial_probe_qcdm (MMPortProbe *self) (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response, self); return FALSE; } +/***************************************************************/ +/* AT */ + +static const gchar *non_at_strings[] = { + /* Option Icera-based devices */ + "option/faema_", + "os_logids.h", + /* Sierra CnS port */ + "NETWORK SERVICE CHANGE", + "/SRC/AMSS", + NULL +}; + +static const guint8 zerobuf[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static gboolean +is_non_at_response (const guint8 *data, gsize len) +{ + const gchar **iter; + size_t iter_len; + int i; + + /* Some devices (observed on a ZTE branded "QUALCOMM INCORPORATED" model + * "154") spew NULLs from some ports. + */ + if ( (len >= sizeof (zerobuf)) + && (memcmp (data, zerobuf, sizeof (zerobuf)) == 0)) + return TRUE; + + /* Check for a well-known non-AT response. There are some ports (eg many + * Icera-based chipsets, Qualcomm Gobi devices before their firmware is + * loaded, Sierra CnS ports) that just shouldn't be probed for AT capability + * if we get a certain response since that response means they aren't AT + * ports. Also, kernel bugs (at least with 2.6.31 and 2.6.32) trigger port + * flow control kernel oopses if we read too much data for these ports. + */ + for (iter = &non_at_strings[0]; iter && *iter; iter++) { + /* Search in the response for the item; the response could have embedded + * nulls so we can't use memcmp() or strstr() on the whole response. + */ + iter_len = strlen (*iter); + for (i = 0; (len >= iter_len) && (i < len - iter_len); i++) { + if (!memcmp (&data[i], *iter, iter_len)) + return TRUE; + } + } + + return FALSE; +} + static void serial_probe_at_icera_result_processor (MMPortProbe *self, GVariant *result) { if (result) { /* If any result given, it must be a string */ @@ -651,12 +710,22 @@ serial_probe_at_parse_response (MMAtSerialPort *port, g_udev_device_get_name (self->priv->port)); task->at_result_processor (self, NULL); serial_probe_schedule (self); return; } + /* Early-abort AT probing if we get a response that indicates this is + * certainly not an AT-capable port. + */ + if (response && is_non_at_response ((const guint8 *) response->str, response->len)) { + task->at_result_processor (self, NULL); + mm_port_probe_set_result_at (self, FALSE); + serial_probe_schedule (self); + return; + } + if (!task->at_commands->response_processor (task->at_commands->command, response ? response->str : NULL, !!task->at_commands[1].command, error, &result, &result_error)) { @@ -774,12 +843,14 @@ at_custom_init_ready (MMPortProbe *self, /* Keep on with remaining probings */ task->at_custom_init_run = TRUE; serial_probe_schedule (self); } +/***************************************************************/ + static void serial_probe_schedule (MMPortProbe *self) { PortProbeRunTask *task = self->priv->task; /* If already cancelled, do nothing else */ @@ -859,75 +930,21 @@ serial_flash_done (MMSerialPort *port, MMPortProbe *self) { /* Schedule probing */ serial_probe_schedule (self); } -static const gchar *dq_strings[] = { - /* Option Icera-based devices */ - "option/faema_", - "os_logids.h", - /* Sierra CnS port */ - "NETWORK SERVICE CHANGE", - "/SRC/AMSS", - NULL -}; - -static const guint8 zerobuf[32] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - static void serial_buffer_full (MMSerialPort *serial, GByteArray *buffer, MMPortProbe *self) { - const gchar **iter; - size_t iter_len; - int i; - - /* Some devices (observed on a ZTE branded "QUALCOMM INCORPORATED" model - * "154") spew NULLs from some ports. - */ - if ( (buffer->len >= sizeof (zerobuf)) - && (memcmp (buffer->data, zerobuf, sizeof (zerobuf)) == 0)) { + if (is_non_at_response (buffer->data, buffer->len)) { mm_serial_port_close (serial); - port_probe_run_task_complete (self->priv->task, - FALSE, - g_error_new_literal (MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Probing stopped due to non-AT response")); - return; - } - - /* Check for an immediate disqualification response. There are some - * ports (Option Icera-based chipsets have them, as do Qualcomm Gobi - * devices before their firmware is loaded) that just shouldn't be - * probed if we get a certain response because we know they can't be - * used. Kernel bugs (at least with 2.6.31 and 2.6.32) also trigger port - * flow control kernel oopses if we read too much data for these ports. - */ - - for (iter = &dq_strings[0]; iter && *iter; iter++) { - /* Search in the response for the item; the response could have embedded - * nulls so we can't use memcmp() or strstr() on the whole response. - */ - iter_len = strlen (*iter); - for (i = 0; i < buffer->len - iter_len; i++) { - if (!memcmp (&buffer->data[i], *iter, iter_len)) { - /* Immediately close the port and complete probing */ - mm_serial_port_close (serial); - port_probe_run_task_complete (self->priv->task, - FALSE, - g_error_new_literal (MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Probing stopped due to non-AT response")); - return; - } - } + mm_port_probe_set_result_at (self, FALSE); + serial_probe_schedule (self); } } static gboolean serial_open_at (MMPortProbe *self) { |