port-probe: launch QMI probing on cdc-wdm ports

Some devices may export cdc-wdm ports talking AT. We need to explicitly check
for QMI protocol support on the cdc-wdm ports before assuming they are QMI.
This commit is contained in:
Aleksander Morgado
2012-06-27 13:57:34 +02:00
parent 3e251129c2
commit db99f340c8
3 changed files with 157 additions and 25 deletions

View File

@@ -589,10 +589,8 @@ mm_plugin_supports_port (MMPlugin *self,
g_udev_device_get_subsystem (port),
g_udev_device_get_name (port));
/* Before launching any probing, check if the port is a net OR a wdm device
* (which cannot be probed). */
if (g_str_equal (g_udev_device_get_subsystem (port), "net") ||
g_str_equal (g_udev_device_get_subsystem (port), "cdc-wdm")) {
/* Before launching any probing, check if the port is a net device. */
if (g_str_equal (g_udev_device_get_subsystem (port), "net")) {
g_simple_async_result_set_op_res_gpointer (
async_result,
GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED),
@@ -602,19 +600,25 @@ mm_plugin_supports_port (MMPlugin *self,
}
/* Build flags depending on what probing needed */
probe_run_flags = MM_PORT_PROBE_NONE;
if (self->priv->at)
probe_run_flags |= MM_PORT_PROBE_AT;
else if (self->priv->single_at)
probe_run_flags |= MM_PORT_PROBE_AT;
if (need_vendor_probing)
probe_run_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_VENDOR);
if (need_product_probing)
probe_run_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_PRODUCT);
if (self->priv->qcdm)
probe_run_flags |= MM_PORT_PROBE_QCDM;
if (self->priv->icera_probe || self->priv->allowed_icera || self->priv->forbidden_icera)
probe_run_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_ICERA);
if (!g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm")) {
/* Serial ports... */
probe_run_flags = MM_PORT_PROBE_NONE;
if (self->priv->at)
probe_run_flags |= MM_PORT_PROBE_AT;
else if (self->priv->single_at)
probe_run_flags |= MM_PORT_PROBE_AT;
if (need_vendor_probing)
probe_run_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_VENDOR);
if (need_product_probing)
probe_run_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_PRODUCT);
if (self->priv->qcdm)
probe_run_flags |= MM_PORT_PROBE_QCDM;
if (self->priv->icera_probe || self->priv->allowed_icera || self->priv->forbidden_icera)
probe_run_flags |= (MM_PORT_PROBE_AT | MM_PORT_PROBE_AT_ICERA);
} else {
/* cdc-wdm ports... */
probe_run_flags = MM_PORT_PROBE_QMI;
}
g_assert (probe_run_flags != MM_PORT_PROBE_NONE);

View File

@@ -24,6 +24,7 @@
#include "mm-port-probe.h"
#include "mm-log.h"
#include "mm-qmi-port.h"
#include "mm-at-serial-port.h"
#include "mm-serial-port.h"
#include "mm-serial-parsers.h"
@@ -44,6 +45,8 @@
* |----> Is Icera?
* ----> QCDM Serial Open
* |----> QCDM?
* ----> QMI Device Open
* |----> QMI Version Info check
*/
G_DEFINE_TYPE (MMPortProbe, mm_port_probe, G_TYPE_OBJECT)
@@ -59,15 +62,21 @@ static GParamSpec *properties[PROP_LAST];
typedef struct {
/* ---- Generic task context ---- */
GSimpleAsyncResult *result;
GCancellable *cancellable;
GCancellable *at_probing_cancellable;
guint32 flags;
guint source_id;
/* ---- Serial probing specific context ---- */
guint buffer_full_id;
MMSerialPort *serial;
/* ---- AT probing specific context ---- */
GCancellable *at_probing_cancellable;
/* Send delay for AT commands */
guint64 at_send_delay;
/* Number of times we tried to open the AT port */
guint at_open_tries;
@@ -82,6 +91,9 @@ typedef struct {
/* Current AT Result processor */
void (* at_result_processor) (MMPortProbe *self,
GVariant *result);
/* ---- QMI probing specific context ---- */
MMQmiPort *qmi_port;
} PortProbeRunTask;
struct _MMPortProbePrivate {
@@ -96,6 +108,7 @@ struct _MMPortProbePrivate {
gchar *vendor;
gchar *product;
gboolean is_icera;
gboolean is_qmi;
/* Current probing task. Only one can be available at a time */
PortProbeRunTask *task;
@@ -113,9 +126,10 @@ mm_port_probe_set_result_at (MMPortProbe *self,
g_udev_device_get_subsystem (self->priv->port),
g_udev_device_get_name (self->priv->port));
/* Also set as not a QCDM port */
/* Also set as not a QCDM/QMI port */
self->priv->is_qcdm = FALSE;
self->priv->flags |= MM_PORT_PROBE_QCDM;
self->priv->is_qmi = FALSE;
self->priv->flags |= (MM_PORT_PROBE_QCDM | MM_PORT_PROBE_QMI);
} else {
mm_dbg ("(%s/%s) port is not AT-capable",
g_udev_device_get_subsystem (self->priv->port),
@@ -199,21 +213,51 @@ mm_port_probe_set_result_qcdm (MMPortProbe *self,
g_udev_device_get_subsystem (self->priv->port),
g_udev_device_get_name (self->priv->port));
/* Also set as not an AT port */
/* Also set as not an AT/QMI port */
self->priv->is_at = FALSE;
self->priv->is_qmi = FALSE;
self->priv->vendor = NULL;
self->priv->product = NULL;
self->priv->is_icera = FALSE;
self->priv->flags |= (MM_PORT_PROBE_AT |
MM_PORT_PROBE_AT_VENDOR |
MM_PORT_PROBE_AT_PRODUCT |
MM_PORT_PROBE_AT_ICERA);
MM_PORT_PROBE_AT_ICERA |
MM_PORT_PROBE_QMI);
} else
mm_dbg ("(%s/%s) port is not QCDM-capable",
g_udev_device_get_subsystem (self->priv->port),
g_udev_device_get_name (self->priv->port));
}
void
mm_port_probe_set_result_qmi (MMPortProbe *self,
gboolean qmi)
{
self->priv->is_qmi = qmi;
self->priv->flags |= MM_PORT_PROBE_QMI;
if (self->priv->is_qmi) {
mm_dbg ("(%s/%s) port is QMI-capable",
g_udev_device_get_subsystem (self->priv->port),
g_udev_device_get_name (self->priv->port));
/* Also set as not an AT/QCDM port */
self->priv->is_at = FALSE;
self->priv->is_qcdm = FALSE;
self->priv->vendor = NULL;
self->priv->product = NULL;
self->priv->flags |= (MM_PORT_PROBE_AT |
MM_PORT_PROBE_AT_VENDOR |
MM_PORT_PROBE_AT_PRODUCT |
MM_PORT_PROBE_AT_ICERA |
MM_PORT_PROBE_QCDM);
} else
mm_dbg ("(%s/%s) port is not QMI-capable",
g_udev_device_get_subsystem (self->priv->port),
g_udev_device_get_name (self->priv->port));
}
static gboolean serial_probe_at (MMPortProbe *self);
static gboolean serial_probe_qcdm (MMPortProbe *self);
static void serial_probe_schedule (MMPortProbe *self);
@@ -233,6 +277,12 @@ port_probe_run_task_free (PortProbeRunTask *task)
g_object_unref (task->serial);
}
if (task->qmi_port) {
if (mm_qmi_port_is_open (task->qmi_port))
mm_qmi_port_close (task->qmi_port);
g_object_unref (task->qmi_port);
}
if (task->cancellable)
g_object_unref (task->cancellable);
if (task->at_probing_cancellable)
@@ -288,6 +338,55 @@ port_probe_run_is_cancelled (MMPortProbe *self)
return FALSE;
}
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);
}
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;
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,
NULL,
(GAsyncReadyCallback)qmi_port_open_ready,
self);
return FALSE;
}
static void
serial_probe_qcdm_parse_response (MMQcdmSerialPort *port,
GByteArray *response,
@@ -957,7 +1056,7 @@ mm_port_probe_run (MMPortProbe *self,
/* 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)) {
for (i = MM_PORT_PROBE_AT; i <= MM_PORT_PROBE_QMI; i = (i << 1)) {
if ((flags & i) && !(self->priv->flags & i)) {
task->flags += i;
}
@@ -993,12 +1092,18 @@ mm_port_probe_run (MMPortProbe *self,
return;
}
/* Otherwise, start by opening as QCDM port */
/* If QCDM probing needed, 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;
}
/* If QMI probing needed, start by opening as a QMI port */
if (task->flags & MM_PORT_PROBE_QMI) {
task->source_id = g_idle_add ((GSourceFunc)wdm_probe_qmi, self);
return;
}
/* Shouldn't happen */
g_assert_not_reached ();
}
@@ -1058,6 +1163,24 @@ mm_port_probe_is_qcdm (MMPortProbe *self)
FALSE);
}
gboolean
mm_port_probe_is_qmi (MMPortProbe *self)
{
const gchar *subsys;
const gchar *name;
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
subsys = g_udev_device_get_subsystem (self->priv->port);
name = g_udev_device_get_name (self->priv->port);
if (!g_str_equal (subsys, "usb") ||
!name ||
!g_str_has_prefix (name, "cdc-wdm"))
return FALSE;
return self->priv->is_qmi;
}
MMPortType
mm_port_probe_get_port_type (MMPortProbe *self)
{
@@ -1073,7 +1196,8 @@ mm_port_probe_get_port_type (MMPortProbe *self)
return MM_PORT_TYPE_NET;
if (g_str_equal (subsys, "usb") &&
g_str_has_prefix (name, "cdc-wdm"))
g_str_has_prefix (name, "cdc-wdm") &&
self->priv->is_qmi)
return MM_PORT_TYPE_QMI;
if (self->priv->flags & MM_PORT_PROBE_QCDM &&

View File

@@ -41,6 +41,7 @@ typedef enum { /*< underscore_name=mm_port_probe_flag >*/
MM_PORT_PROBE_AT_PRODUCT = 1 << 2,
MM_PORT_PROBE_AT_ICERA = 1 << 3,
MM_PORT_PROBE_QCDM = 1 << 4,
MM_PORT_PROBE_QMI = 1 << 5
} MMPortProbeFlag;
typedef struct _MMPortProbe MMPortProbe;
@@ -95,6 +96,8 @@ void mm_port_probe_set_result_at_icera (MMPortProbe *self,
gboolean is_icera);
void mm_port_probe_set_result_qcdm (MMPortProbe *self,
gboolean qcdm);
void mm_port_probe_set_result_qmi (MMPortProbe *self,
gboolean qmi);
/* Run probing */
void mm_port_probe_run (MMPortProbe *self,
@@ -115,6 +118,7 @@ gboolean mm_port_probe_run_cancel_at_probing (MMPortProbe *self);
MMPortType mm_port_probe_get_port_type (MMPortProbe *self);
gboolean mm_port_probe_is_at (MMPortProbe *self);
gboolean mm_port_probe_is_qcdm (MMPortProbe *self);
gboolean mm_port_probe_is_qmi (MMPortProbe *self);
const gchar *mm_port_probe_get_vendor (MMPortProbe *self);
const gchar *mm_port_probe_get_product (MMPortProbe *self);
gboolean mm_port_probe_is_icera (MMPortProbe *self);