plugin: allow cancellation of the port probe operation
This commit is contained in:
@@ -465,6 +465,7 @@ plugin_supports_port_ready (MMPlugin *plugin,
|
|||||||
/* Get supports check results */
|
/* Get supports check results */
|
||||||
support_result = mm_plugin_supports_port_finish (plugin, res, &error);
|
support_result = mm_plugin_supports_port_finish (plugin, res, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
g_assert_cmpuint (support_result, ==, MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
|
||||||
mm_warn ("[plugin manager] task %s: error when checking support with plugin '%s': '%s'",
|
mm_warn ("[plugin manager] task %s: error when checking support with plugin '%s': '%s'",
|
||||||
port_context->name, mm_plugin_get_name (plugin), error->message);
|
port_context->name, mm_plugin_get_name (plugin), error->message);
|
||||||
g_error_free (error);
|
g_error_free (error);
|
||||||
@@ -474,6 +475,7 @@ plugin_supports_port_ready (MMPlugin *plugin,
|
|||||||
case MM_PLUGIN_SUPPORTS_PORT_SUPPORTED:
|
case MM_PLUGIN_SUPPORTS_PORT_SUPPORTED:
|
||||||
port_context_supported (port_context, plugin);
|
port_context_supported (port_context, plugin);
|
||||||
break;
|
break;
|
||||||
|
case MM_PLUGIN_SUPPORTS_PORT_UNKNOWN:
|
||||||
case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
|
case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
|
||||||
port_context_unsupported (port_context, plugin);
|
port_context_unsupported (port_context, plugin);
|
||||||
break;
|
break;
|
||||||
@@ -518,6 +520,7 @@ port_context_next (PortContext *port_context)
|
|||||||
mm_plugin_supports_port (plugin,
|
mm_plugin_supports_port (plugin,
|
||||||
port_context->device,
|
port_context->device,
|
||||||
port_context->port,
|
port_context->port,
|
||||||
|
port_context->cancellable,
|
||||||
(GAsyncReadyCallback) plugin_supports_port_ready,
|
(GAsyncReadyCallback) plugin_supports_port_ready,
|
||||||
port_context_ref (port_context));
|
port_context_ref (port_context));
|
||||||
}
|
}
|
||||||
|
224
src/mm-plugin.c
224
src/mm-plugin.c
@@ -582,125 +582,119 @@ apply_post_probing_filters (MMPlugin *self,
|
|||||||
|
|
||||||
/* Context for the asynchronous probing operation */
|
/* Context for the asynchronous probing operation */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GSimpleAsyncResult *result;
|
|
||||||
MMPlugin *self;
|
MMPlugin *self;
|
||||||
MMPortProbeFlag flags;
|
|
||||||
MMDevice *device;
|
MMDevice *device;
|
||||||
|
MMPortProbeFlag flags;
|
||||||
} PortProbeRunContext;
|
} PortProbeRunContext;
|
||||||
|
|
||||||
|
static void
|
||||||
|
port_probe_run_context_free (PortProbeRunContext *ctx)
|
||||||
|
{
|
||||||
|
g_object_unref (ctx->device);
|
||||||
|
g_object_unref (ctx->self);
|
||||||
|
g_slice_free (PortProbeRunContext, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
port_probe_run_ready (MMPortProbe *probe,
|
port_probe_run_ready (MMPortProbe *probe,
|
||||||
GAsyncResult *probe_result,
|
GAsyncResult *probe_result,
|
||||||
PortProbeRunContext *ctx)
|
GTask *task)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNKNOWN;
|
||||||
|
PortProbeRunContext *ctx;
|
||||||
|
|
||||||
if (!mm_port_probe_run_finish (probe, probe_result, &error)) {
|
if (!mm_port_probe_run_finish (probe, probe_result, &error)) {
|
||||||
/* Probing failed saying the port is unsupported. This is not to be
|
/* Probing failed saying the port is unsupported. This is not to be
|
||||||
* treated as a generic error, the plugin is just telling us as nicely
|
* treated as a generic error, the plugin is just telling us as nicely
|
||||||
* as it can that the port is not supported, so don't warn these cases.
|
* as it can that the port is not supported, so don't warn these cases.
|
||||||
*/
|
*/
|
||||||
if (g_error_matches (error,
|
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
|
||||||
MM_CORE_ERROR,
|
result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
|
||||||
MM_CORE_ERROR_UNSUPPORTED)) {
|
goto out;
|
||||||
g_simple_async_result_set_op_res_gpointer (ctx->result,
|
|
||||||
GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED),
|
|
||||||
NULL);
|
|
||||||
g_error_free (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Probing failed but the plugin tells us to retry; so we'll defer the
|
/* Probing failed but the plugin tells us to retry; so we'll defer the
|
||||||
* probing a bit */
|
* probing a bit */
|
||||||
else if (g_error_matches (error,
|
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
|
||||||
MM_CORE_ERROR,
|
result = MM_PLUGIN_SUPPORTS_PORT_DEFER;
|
||||||
MM_CORE_ERROR_RETRY)) {
|
goto out;
|
||||||
g_simple_async_result_set_op_res_gpointer (ctx->result,
|
|
||||||
GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER),
|
|
||||||
NULL);
|
|
||||||
g_error_free (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For remaining errors, just propagate them */
|
/* For remaining errors, just propagate them */
|
||||||
else {
|
g_task_return_error (task, error);
|
||||||
g_simple_async_result_take_error (ctx->result, error);
|
g_object_unref (task);
|
||||||
}
|
return;
|
||||||
} else {
|
|
||||||
/* Probing succeeded */
|
|
||||||
MMPluginSupportsResult supports_result;
|
|
||||||
|
|
||||||
if (!apply_post_probing_filters (ctx->self, ctx->flags, probe)) {
|
|
||||||
/* Port is supported! */
|
|
||||||
supports_result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED;
|
|
||||||
|
|
||||||
/* If we were looking for AT ports, and the port is AT,
|
|
||||||
* and we were told that only one AT port is expected, cancel AT
|
|
||||||
* probings in the other available support tasks of the SAME
|
|
||||||
* device. */
|
|
||||||
if (ctx->self->priv->single_at &&
|
|
||||||
ctx->flags & MM_PORT_PROBE_AT &&
|
|
||||||
mm_port_probe_is_at (probe)) {
|
|
||||||
GList *l;
|
|
||||||
|
|
||||||
for (l = mm_device_peek_port_probe_list (ctx->device); l; l = g_list_next (l)) {
|
|
||||||
if (l->data != probe) {
|
|
||||||
mm_port_probe_run_cancel_at_probing (MM_PORT_PROBE (l->data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Unsupported port, remove from internal tracking HT */
|
|
||||||
supports_result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_simple_async_result_set_op_res_gpointer (ctx->result,
|
|
||||||
GUINT_TO_POINTER (supports_result),
|
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Complete the async supports port request */
|
/* Probing succeeded, recover context */
|
||||||
g_simple_async_result_complete_in_idle (ctx->result);
|
ctx = g_task_get_task_data (task);
|
||||||
|
|
||||||
g_object_unref (ctx->device);
|
/* Apply post probing filters */
|
||||||
g_object_unref (ctx->result);
|
if (!apply_post_probing_filters (ctx->self, ctx->flags, probe)) {
|
||||||
g_object_unref (ctx->self);
|
/* Port is supported! */
|
||||||
g_free (ctx);
|
result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED;
|
||||||
|
|
||||||
|
/* If we were looking for AT ports, and the port is AT,
|
||||||
|
* and we were told that only one AT port is expected, cancel AT
|
||||||
|
* probings in the other available support tasks of the SAME
|
||||||
|
* device. */
|
||||||
|
if (ctx->self->priv->single_at &&
|
||||||
|
ctx->flags & MM_PORT_PROBE_AT &&
|
||||||
|
mm_port_probe_is_at (probe)) {
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = mm_device_peek_port_probe_list (ctx->device); l; l = g_list_next (l)) {
|
||||||
|
if (l->data != probe)
|
||||||
|
mm_port_probe_run_cancel_at_probing (MM_PORT_PROBE (l->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
/* Filtered by post probing filters */
|
||||||
|
result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
|
||||||
|
|
||||||
|
out:
|
||||||
|
/* Complete action */
|
||||||
|
g_clear_error (&error);
|
||||||
|
g_task_return_int (task, result);
|
||||||
|
g_object_unref (task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
G_STATIC_ASSERT (MM_PLUGIN_SUPPORTS_PORT_UNKNOWN == -1);
|
||||||
|
|
||||||
MMPluginSupportsResult
|
MMPluginSupportsResult
|
||||||
mm_plugin_supports_port_finish (MMPlugin *self,
|
mm_plugin_supports_port_finish (MMPlugin *self,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (MM_IS_PLUGIN (self),
|
g_return_val_if_fail (MM_IS_PLUGIN (self), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
|
||||||
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
|
g_return_val_if_fail (G_IS_TASK (result), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
|
||||||
g_return_val_if_fail (G_IS_ASYNC_RESULT (result),
|
|
||||||
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
|
|
||||||
|
|
||||||
/* Propagate error, if any */
|
return (MMPluginSupportsResult) g_task_propagate_int (G_TASK (result), error);
|
||||||
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) {
|
|
||||||
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (MMPluginSupportsResult) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
mm_plugin_supports_port (MMPlugin *self,
|
mm_plugin_supports_port (MMPlugin *self,
|
||||||
MMDevice *device,
|
MMDevice *device,
|
||||||
GUdevDevice *port,
|
GUdevDevice *port,
|
||||||
GAsyncReadyCallback callback,
|
GCancellable *cancellable,
|
||||||
gpointer user_data)
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
MMPortProbe *probe = NULL;
|
MMPortProbe *probe = NULL;
|
||||||
GSimpleAsyncResult *async_result;
|
GTask *task;
|
||||||
PortProbeRunContext *ctx;
|
PortProbeRunContext *ctx;
|
||||||
gboolean need_vendor_probing;
|
gboolean need_vendor_probing;
|
||||||
gboolean need_product_probing;
|
gboolean need_product_probing;
|
||||||
MMPortProbeFlag probe_run_flags;
|
MMPortProbeFlag probe_run_flags;
|
||||||
gchar *probe_list_str;
|
gchar *probe_list_str;
|
||||||
|
|
||||||
async_result = g_simple_async_result_new (G_OBJECT (self),
|
g_return_if_fail (MM_IS_PLUGIN (self));
|
||||||
callback,
|
g_return_if_fail (MM_IS_DEVICE (device));
|
||||||
user_data,
|
g_return_if_fail (G_UDEV_IS_DEVICE (port));
|
||||||
mm_plugin_supports_port);
|
|
||||||
|
/* Create new cancellable task */
|
||||||
|
task = g_task_new (self, cancellable, callback, user_data);
|
||||||
|
|
||||||
/* Apply filters before launching the probing */
|
/* Apply filters before launching the probing */
|
||||||
if (apply_pre_probing_filters (self,
|
if (apply_pre_probing_filters (self,
|
||||||
@@ -709,46 +703,40 @@ mm_plugin_supports_port (MMPlugin *self,
|
|||||||
&need_vendor_probing,
|
&need_vendor_probing,
|
||||||
&need_product_probing)) {
|
&need_product_probing)) {
|
||||||
/* Filtered! */
|
/* Filtered! */
|
||||||
g_simple_async_result_set_op_res_gpointer (async_result,
|
g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
|
||||||
GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED),
|
g_object_unref (task);
|
||||||
NULL);
|
return;
|
||||||
g_simple_async_result_complete_in_idle (async_result);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Need to launch new probing */
|
/* Need to launch new probing */
|
||||||
probe = MM_PORT_PROBE (mm_device_get_port_probe (device, port));
|
probe = MM_PORT_PROBE (mm_device_peek_port_probe (device, port));
|
||||||
if (!probe) {
|
if (!probe) {
|
||||||
/* This may happen if the ports get removed from the device while
|
/* This may happen if the ports get removed from the device while
|
||||||
* probing is ongoing */
|
* probing is ongoing */
|
||||||
g_simple_async_result_set_error (async_result,
|
g_task_return_new_error (task,
|
||||||
MM_CORE_ERROR,
|
MM_CORE_ERROR,
|
||||||
MM_CORE_ERROR_FAILED,
|
MM_CORE_ERROR_FAILED,
|
||||||
"(%s) Missing port probe for port (%s/%s)",
|
"(%s) Missing port probe for port (%s/%s)",
|
||||||
self->priv->name,
|
self->priv->name,
|
||||||
g_udev_device_get_subsystem (port),
|
g_udev_device_get_subsystem (port),
|
||||||
g_udev_device_get_name (port));
|
g_udev_device_get_name (port));
|
||||||
g_simple_async_result_complete_in_idle (async_result);
|
g_object_unref (task);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Before launching any probing, check if the port is a net device. */
|
/* Before launching any probing, check if the port is a net device. */
|
||||||
if (g_str_equal (g_udev_device_get_subsystem (port), "net")) {
|
if (g_str_equal (g_udev_device_get_subsystem (port), "net")) {
|
||||||
mm_dbg ("(%s) [%s] probing deferred until result suggested",
|
mm_dbg ("(%s) [%s] probing deferred until result suggested",
|
||||||
self->priv->name,
|
self->priv->name, g_udev_device_get_name (port));
|
||||||
g_udev_device_get_name (port));
|
g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED);
|
||||||
g_simple_async_result_set_op_res_gpointer (
|
g_object_unref (task);
|
||||||
async_result,
|
return;
|
||||||
GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED),
|
|
||||||
NULL);
|
|
||||||
g_simple_async_result_complete_in_idle (async_result);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build flags depending on what probing needed */
|
/* Build flags depending on what probing needed */
|
||||||
|
probe_run_flags = MM_PORT_PROBE_NONE;
|
||||||
if (!g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm")) {
|
if (!g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm")) {
|
||||||
/* Serial ports... */
|
/* Serial ports... */
|
||||||
probe_run_flags = MM_PORT_PROBE_NONE;
|
|
||||||
if (self->priv->at)
|
if (self->priv->at)
|
||||||
probe_run_flags |= MM_PORT_PROBE_AT;
|
probe_run_flags |= MM_PORT_PROBE_AT;
|
||||||
else if (self->priv->single_at)
|
else if (self->priv->single_at)
|
||||||
@@ -757,7 +745,6 @@ mm_plugin_supports_port (MMPlugin *self,
|
|||||||
probe_run_flags |= MM_PORT_PROBE_QCDM;
|
probe_run_flags |= MM_PORT_PROBE_QCDM;
|
||||||
} else {
|
} else {
|
||||||
/* cdc-wdm ports... */
|
/* cdc-wdm ports... */
|
||||||
probe_run_flags = MM_PORT_PROBE_NONE;
|
|
||||||
if (self->priv->qmi && !g_strcmp0 (mm_device_utils_get_port_driver (port), "qmi_wwan"))
|
if (self->priv->qmi && !g_strcmp0 (mm_device_utils_get_port_driver (port), "qmi_wwan"))
|
||||||
probe_run_flags |= MM_PORT_PROBE_QMI;
|
probe_run_flags |= MM_PORT_PROBE_QMI;
|
||||||
else if (self->priv->mbim && !g_strcmp0 (mm_device_utils_get_port_driver (port), "cdc_mbim"))
|
else if (self->priv->mbim && !g_strcmp0 (mm_device_utils_get_port_driver (port), "cdc_mbim"))
|
||||||
@@ -779,11 +766,9 @@ mm_plugin_supports_port (MMPlugin *self,
|
|||||||
/* If no explicit probing was required, just request to grab it without probing anything.
|
/* If no explicit probing was required, just request to grab it without probing anything.
|
||||||
* This may happen, e.g. with cdc-wdm ports which do not need QMI/MBIM probing. */
|
* This may happen, e.g. with cdc-wdm ports which do not need QMI/MBIM probing. */
|
||||||
if (probe_run_flags == MM_PORT_PROBE_NONE) {
|
if (probe_run_flags == MM_PORT_PROBE_NONE) {
|
||||||
g_simple_async_result_set_op_res_gpointer (async_result,
|
g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED);
|
||||||
GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED),
|
g_object_unref (task);
|
||||||
NULL);
|
return;
|
||||||
g_simple_async_result_complete_in_idle (async_result);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a modem is already available and the plugin says that only one AT port is
|
/* If a modem is already available and the plugin says that only one AT port is
|
||||||
@@ -803,11 +788,13 @@ mm_plugin_supports_port (MMPlugin *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Setup async call context */
|
/* Setup async call context */
|
||||||
ctx = g_new (PortProbeRunContext, 1);
|
ctx = g_slice_new0 (PortProbeRunContext);
|
||||||
ctx->self = g_object_ref (self);
|
ctx->self = g_object_ref (self);
|
||||||
ctx->device = g_object_ref (device);
|
ctx->device = g_object_ref (device);
|
||||||
ctx->result = g_object_ref (async_result);
|
ctx->flags = probe_run_flags;
|
||||||
ctx->flags = probe_run_flags;
|
|
||||||
|
/* Store context in task */
|
||||||
|
g_task_set_task_data (task, ctx, (GDestroyNotify) port_probe_run_context_free);
|
||||||
|
|
||||||
/* Launch the probe */
|
/* Launch the probe */
|
||||||
probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
|
probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
|
||||||
@@ -824,13 +811,8 @@ mm_plugin_supports_port (MMPlugin *self,
|
|||||||
self->priv->send_lf,
|
self->priv->send_lf,
|
||||||
self->priv->custom_at_probe,
|
self->priv->custom_at_probe,
|
||||||
self->priv->custom_init,
|
self->priv->custom_init,
|
||||||
(GAsyncReadyCallback)port_probe_run_ready,
|
(GAsyncReadyCallback) port_probe_run_ready,
|
||||||
ctx);
|
task);
|
||||||
|
|
||||||
out:
|
|
||||||
g_object_unref (async_result);
|
|
||||||
if (probe)
|
|
||||||
g_object_unref (probe);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@@ -64,7 +64,8 @@
|
|||||||
#define MM_PLUGIN_SEND_LF "send-lf"
|
#define MM_PLUGIN_SEND_LF "send-lf"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED = 0x0,
|
MM_PLUGIN_SUPPORTS_PORT_UNKNOWN = -1,
|
||||||
|
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED,
|
||||||
MM_PLUGIN_SUPPORTS_PORT_DEFER,
|
MM_PLUGIN_SUPPORTS_PORT_DEFER,
|
||||||
MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED,
|
MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED,
|
||||||
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
|
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
|
||||||
@@ -119,14 +120,15 @@ MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *plugin,
|
|||||||
MMDevice *device,
|
MMDevice *device,
|
||||||
GUdevDevice *port);
|
GUdevDevice *port);
|
||||||
|
|
||||||
void mm_plugin_supports_port (MMPlugin *plugin,
|
void mm_plugin_supports_port (MMPlugin *plugin,
|
||||||
MMDevice *device,
|
MMDevice *device,
|
||||||
GUdevDevice *port,
|
GUdevDevice *port,
|
||||||
GAsyncReadyCallback callback,
|
GCancellable *cancellable,
|
||||||
gpointer user_data);
|
GAsyncReadyCallback callback,
|
||||||
MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *plugin,
|
gpointer user_data);
|
||||||
GAsyncResult *result,
|
MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *plugin,
|
||||||
GError **error);
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
MMBaseModem *mm_plugin_create_modem (MMPlugin *plugin,
|
MMBaseModem *mm_plugin_create_modem (MMPlugin *plugin,
|
||||||
MMDevice *device,
|
MMDevice *device,
|
||||||
|
Reference in New Issue
Block a user