Example #1
0
static gboolean
wdm_probe_qmi (MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;

    /* If already cancelled, do nothing else */
    if (port_probe_run_is_cancelled (self))
        return FALSE;

#if defined WITH_QMI
    mm_dbg ("(%s/%s) probing QMI...",
            g_udev_device_get_subsystem (self->priv->port),
            g_udev_device_get_name (self->priv->port));

    /* Create a port and try to open it */
    task->qmi_port = mm_qmi_port_new (g_udev_device_get_name (self->priv->port));
    mm_qmi_port_open (task->qmi_port,
                      FALSE,
                      NULL,
                      (GAsyncReadyCallback)qmi_port_open_ready,
                      self);
#else
    /* If not compiled with QMI support, just assume we won't have any QMI port */
    mm_port_probe_set_result_qmi (self, FALSE);
    port_probe_run_task_complete (task, TRUE, NULL);
#endif /* WITH_QMI */

    return FALSE;
}
Example #2
0
static void
qmi_port_open_ready (MMQmiPort *qmi_port,
                     GAsyncResult *res,
                     MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;
    GError *error = NULL;
    gboolean is_qmi;

    is_qmi = mm_qmi_port_open_finish (qmi_port, res, &error);
    if (!is_qmi) {
        mm_dbg ("(%s/%s) error checking QMI support: '%s'",
                g_udev_device_get_subsystem (self->priv->port),
                g_udev_device_get_name (self->priv->port),
                error ? error->message : "unknown error");
        g_clear_error (&error);
    }

    /* Set probing result */
    mm_port_probe_set_result_qmi (self, is_qmi);

    mm_qmi_port_close (qmi_port);

    /* All done! Finish asynchronously */
    port_probe_run_task_complete (task, TRUE, NULL);
}
Example #3
0
static void
serial_probe_schedule (MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;

    /* If already cancelled, do nothing else */
    if (port_probe_run_is_cancelled (self))
        return;

    /* Cleanup */
    task->at_result_processor = NULL;
    task->at_commands = NULL;

    /* If we got some custom initialization commands requested, go on with them
     * first. */
    if (task->at_custom_init) {
        task->at_result_processor = serial_probe_at_custom_init_result_processor;
        task->at_commands = task->at_custom_init;
    }
    /* AT check requested and not already probed? */
    else if ((task->flags & MM_PORT_PROBE_AT) &&
             !(self->priv->flags & MM_PORT_PROBE_AT)) {
        /* Prepare AT probing */
        task->at_result_processor = serial_probe_at_result_processor;
        task->at_commands = at_probing;
    }
    /* Vendor requested and not already probed? */
    else if ((task->flags & MM_PORT_PROBE_AT_VENDOR) &&
        !(self->priv->flags & MM_PORT_PROBE_AT_VENDOR)) {
        /* Prepare AT vendor probing */
        task->at_result_processor = serial_probe_at_vendor_result_processor;
        task->at_commands = vendor_probing;
    }
    /* Product requested and not already probed? */
    else if ((task->flags & MM_PORT_PROBE_AT_PRODUCT) &&
             !(self->priv->flags & MM_PORT_PROBE_AT_PRODUCT)) {
        /* Prepare AT product probing */
        task->at_result_processor = serial_probe_at_product_result_processor;
        task->at_commands = product_probing;
    }

    /* If a next AT group detected, go for it */
    if (task->at_result_processor &&
        task->at_commands) {
        task->source_id = g_idle_add ((GSourceFunc)serial_probe_at, self);
        return;
    }

    /* QCDM requested and not already probed? */
    if ((task->flags & MM_PORT_PROBE_QCDM) &&
        !(self->priv->flags & MM_PORT_PROBE_QCDM)) {
        task->source_id = g_idle_add ((GSourceFunc)serial_probe_qcdm, self);
        return;
    }

    /* All done! Finish asynchronously */
    port_probe_run_task_complete (task, TRUE, NULL);
}
Example #4
0
static void
serial_buffer_full (MMSerialPort *serial,
                    GByteArray *buffer,
                    PortProbeRunTask *task)
{
    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)) {
        mm_serial_port_close (serial);
        port_probe_run_task_complete (task, FALSE, NULL);
        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 (task, FALSE, NULL);
                return;
            }
        }
    }
}
Example #5
0
static void
at_custom_init_ready (MMPortProbe *self,
                      GAsyncResult *res)
{
    PortProbeRunTask *task = self->priv->task;
    GError *error = NULL;

    if (!task->at_custom_init_finish (self, res, &error)) {
        /* All errors propagated up end up forcing an UNSUPPORTED result */
        port_probe_run_task_complete (task, FALSE, error);
        return;
    }

    /* Keep on with remaining probings */
    task->at_custom_init_run = TRUE;
    serial_probe_schedule (self);
}
Example #6
0
static gboolean
port_probe_run_is_cancelled (MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;

    /* Manually check if cancelled.
     * TODO: Make the serial port response wait cancellable,
     * so that we can connect a callback to the cancellable and forget about
     * manually checking it.
     */
    if (g_cancellable_is_cancelled (task->cancellable)) {
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_CORE_ERROR,
                         MM_CORE_ERROR_CANCELLED,
                         "(%s) port probing cancelled",
                         self->priv->name));
        return TRUE;
    }

    return FALSE;
}
Example #7
0
void
mm_port_probe_run (MMPortProbe *self,
                   MMPortProbeFlag flags,
                   guint64 at_send_delay,
                   const MMPortProbeAtCommand *at_custom_init,
                   GAsyncReadyCallback callback,
                   gpointer user_data)
{
    PortProbeRunTask *task;
    guint32 i;
    gchar *probe_list_str;

    g_return_if_fail (MM_IS_PORT_PROBE (self));
    g_return_if_fail (flags != MM_PORT_PROBE_NONE);
    g_return_if_fail (callback != NULL);

    /* Shouldn't schedule more than one probing at a time */
    g_assert (self->priv->task == NULL);

    task = g_new0 (PortProbeRunTask, 1);
    task->at_send_delay = at_send_delay;
    task->flags = MM_PORT_PROBE_NONE;
    task->at_custom_init = at_custom_init;
    task->result = g_simple_async_result_new (G_OBJECT (self),
                                              callback,
                                              user_data,
                                              mm_port_probe_run);

    /* Check if we already have the requested probing results.
     * We will fix here the 'task->flags' so that we only request probing
     * for the missing things. */
    for (i = MM_PORT_PROBE_AT; i <= MM_PORT_PROBE_QCDM; i = (i << 1)) {
        if ((flags & i) && !(self->priv->flags & i)) {
            task->flags += i;
        }
    }

    /* Store as current task. We need to keep it internally, as it will be
     * freed during _finish() when the operation is completed. */
    self->priv->task = task;

    /* All requested probings already available? If so, we're done */
    if (!task->flags) {
        port_probe_run_task_complete (task, TRUE, NULL);
        return;
    }

    /* Setup internal cancellable */
    task->cancellable = g_cancellable_new ();

    probe_list_str = mm_port_probe_flag_build_string_from_mask (task->flags);
    mm_info ("(%s) launching port probing: '%s'",
             self->priv->name,
             probe_list_str);
    g_free (probe_list_str);

    /* If any AT probing is needed, start by opening as AT port */
    if (task->flags & MM_PORT_PROBE_AT ||
        task->flags & MM_PORT_PROBE_AT_VENDOR ||
        task->flags & MM_PORT_PROBE_AT_PRODUCT) {
        task->at_probing_cancellable = g_cancellable_new ();
        task->source_id = g_idle_add ((GSourceFunc)serial_open_at, self);
        return;
    }

    /* Otherwise, start by opening as QCDM port */
    if (task->flags & MM_PORT_PROBE_QCDM) {
        task->source_id = g_idle_add ((GSourceFunc)serial_probe_qcdm, self);
        return;
    }

    /* Shouldn't happen */
    g_assert_not_reached ();
}
Example #8
0
static gboolean
serial_open_at (MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;
    GError *error = NULL;

    task->source_id = 0;

    /* If already cancelled, do nothing else */
    if (port_probe_run_is_cancelled (self))
        return FALSE;

    /* Create AT serial port if not done before */
    if (!task->serial) {
        task->serial = MM_SERIAL_PORT (mm_at_serial_port_new (self->priv->name));
        if (!task->serial) {
            port_probe_run_task_complete (
                task,
                FALSE,
                g_error_new (MM_CORE_ERROR,
                             MM_CORE_ERROR_FAILED,
                             "(%s) couldn't create AT port",
                             self->priv->name));
            return FALSE;
        }

        g_object_set (task->serial,
                      MM_SERIAL_PORT_SEND_DELAY, task->at_send_delay,
                      MM_PORT_CARRIER_DETECT, FALSE,
                      MM_SERIAL_PORT_SPEW_CONTROL, TRUE,
                      NULL);

        mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (task->serial),
                                               mm_serial_parser_v1_parse,
                                               mm_serial_parser_v1_new (),
                                               mm_serial_parser_v1_destroy);
    }

    /* Try to open the port */
    if (!mm_serial_port_open (task->serial, &error)) {
        /* Abort if maximum number of open tries reached */
        if (++task->at_open_tries > 4) {
            /* took too long to open the port; give up */
            port_probe_run_task_complete (
                task,
                FALSE,
                g_error_new (MM_CORE_ERROR,
                             MM_CORE_ERROR_FAILED,
                             "(%s) failed to open port after 4 tries",
                             self->priv->name));
        } else if (g_error_matches (error,
                                    MM_SERIAL_ERROR,
                                    MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) {
            /* this is nozomi being dumb; try again */
            task->source_id = g_timeout_add_seconds (1,
                                                     (GSourceFunc)serial_open_at,
                                                     self);
        } else {
            port_probe_run_task_complete (
                task,
                FALSE,
                g_error_new (MM_SERIAL_ERROR,
                             MM_SERIAL_ERROR_OPEN_FAILED,
                             "(%s) failed to open port: %s",
                             self->priv->name,
                             (error ? error->message : "unknown error")));
        }

        g_clear_error (&error);
        return FALSE;
    }

    /* success, start probing */
    task->buffer_full_id = g_signal_connect (task->serial,
                                             "buffer-full",
                                             G_CALLBACK (serial_buffer_full),
                                             self);

    mm_serial_port_flash (MM_SERIAL_PORT (task->serial),
                          100,
                          TRUE,
                          (MMSerialFlashFn)serial_flash_done,
                          self);
    return FALSE;
}
Example #9
0
static void
serial_probe_at_parse_response (MMAtSerialPort *port,
                                GString *response,
                                GError *error,
                                MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;
    GVariant *result = NULL;
    GError *result_error = NULL;

    /* If already cancelled, do nothing else */
    if (port_probe_run_is_cancelled (self))
        return;

    /* If AT probing cancelled, end this partial probing */
    if (g_cancellable_is_cancelled (task->at_probing_cancellable)) {
        mm_dbg ("(%s) no need to keep on probing the port for AT support",
                self->priv->name);
        task->at_result_processor (self, NULL);
        serial_probe_schedule (self);
        return;
    }

    if (!task->at_commands->response_processor (task->at_commands->command,
                                                response->str,
                                                !!task->at_commands[1].command,
                                                error,
                                                &result,
                                                &result_error)) {
        /* Were we told to abort the whole probing? */
        if (result_error) {
            port_probe_run_task_complete (
                task,
                FALSE,
                g_error_new (MM_CORE_ERROR,
                             MM_CORE_ERROR_UNSUPPORTED,
                             "(%s) error while probing AT features: %s",
                             self->priv->name,
                             result_error->message));
            g_error_free (result_error);
            return;
        }

        /* Go on to next command */
        task->at_commands++;
        if (!task->at_commands->command) {
            /* Was it the last command in the group? If so,
             * end this partial probing */
            task->at_result_processor (self, NULL);
            /* Reschedule */
            serial_probe_schedule (self);
            return;
        }

        /* Schedule the next command in the probing group */
        task->source_id = g_idle_add ((GSourceFunc)serial_probe_at, self);
        return;
    }

    /* Run result processor.
     * Note that custom init commands are allowed to not return anything */
    task->at_result_processor (self, result);
    if (result)
        g_variant_unref (result);

    /* Reschedule probing */
    serial_probe_schedule (self);
}
Example #10
0
static gboolean
serial_probe_qcdm (MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;
    GError *error = NULL;
    GByteArray *verinfo = NULL;
    GByteArray *verinfo2;
    gint len;

    task->source_id = 0;

    /* If already cancelled, do nothing else */
    if (port_probe_run_is_cancelled (self))
        return FALSE;

    mm_dbg ("(%s) probing QCDM...", self->priv->name);

    /* If open, close the AT port */
    if (task->serial) {
        mm_serial_port_close (task->serial);
        g_object_unref (task->serial);
    }

    /* Open the QCDM port */
    task->serial = MM_SERIAL_PORT (mm_qcdm_serial_port_new (self->priv->name));
    if (!task->serial) {
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_CORE_ERROR,
                         MM_CORE_ERROR_FAILED,
                         "(%s) Couldn't create QCDM port",
                         self->priv->name));
        return FALSE;
    }

    /* Try to open the port */
    if (!mm_serial_port_open (task->serial, &error)) {
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_SERIAL_ERROR,
                         MM_SERIAL_ERROR_OPEN_FAILED,
                         "(%s) Failed to open QCDM port: %s",
                         self->priv->name,
                         (error ? error->message : "unknown error")));
        g_clear_error (&error);
        return FALSE;
    }

    /* Build up the probe command */
    verinfo = g_byte_array_sized_new (50);
    len = qcdm_cmd_version_info_new ((gchar *) verinfo->data, 50);
    if (len <= 0) {
        g_byte_array_free (verinfo, TRUE);
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_SERIAL_ERROR,
                         MM_SERIAL_ERROR_OPEN_FAILED,
                         "(%s) Failed to create QCDM versin info command",
                         self->priv->name));
        return FALSE;
    }
    verinfo->len = len;

    /* Queuing the command takes ownership over it; dup it for the second try */
    verinfo2 = g_byte_array_sized_new (verinfo->len);
    g_byte_array_append (verinfo2, verinfo->data, verinfo->len);

    /* Send the command twice; the ports often need to be woken up */
    mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial),
                                       verinfo,
                                       3,
                                       NULL,
                                       (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response,
                                       NULL);
    mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial),
                                       verinfo2,
                                       3,
                                       NULL,
                                       (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response,
                                       self);

    return FALSE;
}
Example #11
0
static gboolean
serial_probe_qcdm (MMPortProbe *self)
{
    PortProbeRunTask *task = self->priv->task;
    GError *error = NULL;
    GByteArray *verinfo = NULL;
    GByteArray *verinfo2;
    gint len;
    guint8 marker = 0x7E;

    task->source_id = 0;

    /* If already cancelled, do nothing else */
    if (port_probe_run_is_cancelled (self))
        return FALSE;

    mm_dbg ("(%s/%s) probing QCDM...",
            g_udev_device_get_subsystem (self->priv->port),
            g_udev_device_get_name (self->priv->port));

    /* If open, close the AT port */
    if (task->serial) {
        mm_serial_port_close (task->serial);
        g_object_unref (task->serial);
    }

    /* Open the QCDM port */
    task->serial = MM_SERIAL_PORT (mm_qcdm_serial_port_new (g_udev_device_get_name (self->priv->port)));
    if (!task->serial) {
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_CORE_ERROR,
                         MM_CORE_ERROR_FAILED,
                         "(%s/%s) Couldn't create QCDM port",
                         g_udev_device_get_subsystem (self->priv->port),
                         g_udev_device_get_name (self->priv->port)));
        return FALSE;
    }

    /* Try to open the port */
    if (!mm_serial_port_open (task->serial, &error)) {
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_SERIAL_ERROR,
                         MM_SERIAL_ERROR_OPEN_FAILED,
                         "(%s/%s) Failed to open QCDM port: %s",
                         g_udev_device_get_subsystem (self->priv->port),
                         g_udev_device_get_name (self->priv->port),
                         (error ? error->message : "unknown error")));
        g_clear_error (&error);
        return FALSE;
    }

    /* Build up the probe command; 0x7E is the frame marker, so put one at the
     * beginning of the buffer to ensure that the device discards any AT
     * commands that probing might have sent earlier.  Should help devices
     * respond more quickly and speed up QCDM probing.
     */
    verinfo = g_byte_array_sized_new (10);
    g_byte_array_append (verinfo, &marker, 1);
    len = qcdm_cmd_version_info_new ((char *) (verinfo->data + 1), 9);
    if (len <= 0) {
        g_byte_array_free (verinfo, TRUE);
        port_probe_run_task_complete (
            task,
            FALSE,
            g_error_new (MM_SERIAL_ERROR,
                         MM_SERIAL_ERROR_OPEN_FAILED,
                         "(%s/%s) Failed to create QCDM versin info command",
                         g_udev_device_get_subsystem (self->priv->port),
                         g_udev_device_get_name (self->priv->port)));
        return FALSE;
    }
    verinfo->len = len + 1;

    /* Queuing the command takes ownership over it; save it for the second try */
    verinfo2 = g_byte_array_sized_new (verinfo->len);
    g_byte_array_append (verinfo2, verinfo->data, verinfo->len);
    g_object_set_data_full (G_OBJECT (self), "cmd2", verinfo2, (GDestroyNotify) g_byte_array_unref);

    mm_qcdm_serial_port_queue_command (MM_QCDM_SERIAL_PORT (task->serial),
                                       verinfo,
                                       3,
                                       NULL,
                                       (MMQcdmSerialResponseFn)serial_probe_qcdm_parse_response,
                                       self);

    return FALSE;
}