plugin-base: rewrite port supports check as fully asynchronous

Before this change, supports check was either synchronous (e.g. in some
UNSUPPORTED cases) or asynchronous (when IN_PROGRESS was returned).

With this fix, the supports check requested to the plugin will always be
completed asynchronously; either directly in an idle before launching any real
probing operation, or once the probing operation is finished.

Therefore, it is not expected to get a IN_PROGRESS reply in
mm_plugin_supports_port_finish(), only UNSUPPORTED|SUPPORTED|DEFERRED.
This commit is contained in:
Aleksander Morgado
2011-09-04 23:59:43 +02:00
committed by Aleksander Morgado
parent d47176a32c
commit dc30536456
4 changed files with 245 additions and 119 deletions

View File

@@ -486,10 +486,9 @@ static void supports_callback (MMPlugin *plugin,
guint32 level, guint32 level,
gpointer user_data); gpointer user_data);
static void try_supports_port (MMManager *manager, static void supports_port_ready_cb (MMPlugin *plugin,
MMPlugin *plugin, GAsyncResult *result,
MMModem *existing, SupportsInfo *info);
SupportsInfo *info);
static gboolean static gboolean
supports_defer_timeout (gpointer user_data) supports_defer_timeout (gpointer user_data)
@@ -500,30 +499,43 @@ supports_defer_timeout (gpointer user_data)
existing = find_modem_for_device (info->manager, info->physdev_path); existing = find_modem_for_device (info->manager, info->physdev_path);
mm_dbg ("(%s): re-checking support...", info->name); mm_dbg ("(%s): re-checking support...", info->name);
try_supports_port (info->manager, mm_plugin_supports_port (MM_PLUGIN (info->cur_plugin->data),
MM_PLUGIN (info->cur_plugin->data), info->subsys,
existing, info->name,
info); info->physdev_path,
existing,
(GAsyncReadyCallback)supports_port_ready_cb,
info);
return FALSE; return FALSE;
} }
static void static void
try_supports_port (MMManager *manager, supports_port_ready_cb (MMPlugin *plugin,
MMPlugin *plugin, GAsyncResult *result,
MMModem *existing, SupportsInfo *info)
SupportsInfo *info)
{ {
MMPluginSupportsResult result; MMPluginSupportsResult supports_result;
GError *error = NULL;
guint level = 0;
result = mm_plugin_supports_port (plugin, /* Get supports check results */
info->subsys, supports_result = mm_plugin_supports_port_finish (plugin,
info->name, result,
info->physdev_path, &level,
existing, &error);
supports_callback, if (error) {
info); mm_warn ("(%s): (%s) error when checking support: '%s'",
mm_plugin_get_name (plugin),
info->name,
error->message);
g_error_free (error);
}
switch (result) { switch (supports_result) {
case MM_PLUGIN_SUPPORTS_PORT_SUPPORTED:
/* Report support level to the callback */
supports_callback (plugin, info->subsys, info->name, level, info);
break;
case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED: case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
/* If the plugin knows it doesn't support the modem, just call the /* If the plugin knows it doesn't support the modem, just call the
* callback and indicate 0 support. * callback and indicate 0 support.
@@ -541,7 +553,8 @@ try_supports_port (MMManager *manager,
info->defer_id = g_timeout_add (3000, supports_defer_timeout, info); info->defer_id = g_timeout_add (3000, supports_defer_timeout, info);
break; break;
case MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS: case MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS:
default: /* This should never happen at this level */
g_warn_if_reached ();
break; break;
} }
} }
@@ -730,7 +743,13 @@ supports_callback (MMPlugin *plugin,
if (next_plugin) { if (next_plugin) {
/* Try the next plugin */ /* Try the next plugin */
try_supports_port (info->manager, next_plugin, existing, info); mm_plugin_supports_port (next_plugin,
info->subsys,
info->name,
info->physdev_path,
existing,
(GAsyncReadyCallback)supports_port_ready_cb,
info);
} else { } else {
/* All done; let the best modem grab the port */ /* All done; let the best modem grab the port */
info->done_id = g_idle_add (do_grab_port, info); info->done_id = g_idle_add (do_grab_port, info);
@@ -895,7 +914,13 @@ device_added (MMManager *manager, GUdevDevice *device)
if (existing) if (existing)
plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG)); plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
try_supports_port (manager, plugin, existing, info); mm_plugin_supports_port (plugin,
info->subsys,
info->name,
info->physdev_path,
existing,
(GAsyncReadyCallback)supports_port_ready_cb,
info);
out: out:
if (physdev) if (physdev)

View File

@@ -149,6 +149,11 @@ G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OB
#define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskPrivate)) #define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskPrivate))
typedef struct {
MMPluginSupportsResult result;
guint support_level;
} SupportsPortResult;
typedef struct { typedef struct {
char *command; char *command;
guint32 tries; guint32 tries;
@@ -181,28 +186,23 @@ typedef struct {
GSList *custom; GSList *custom;
GSList *cur_custom; /* Pointer to current custom init command */ GSList *cur_custom; /* Pointer to current custom init command */
MMSupportsPortResultFunc callback; GSimpleAsyncResult *async_result;
gpointer callback_data;
} MMPluginBaseSupportsTaskPrivate; } MMPluginBaseSupportsTaskPrivate;
static SupportsPortResult *supports_port_result_new (MMPluginSupportsResult result,
guint support_level);
static void supports_port_result_free (SupportsPortResult *spr);
static MMPluginBaseSupportsTask * static MMPluginBaseSupportsTask *
supports_task_new (MMPluginBase *self, supports_task_new (MMPluginBase *self,
GUdevDevice *port, GUdevDevice *port,
const char *physdev_path, const char *physdev_path,
const char *driver, const char *driver,
MMSupportsPortResultFunc callback, GSimpleAsyncResult *result)
gpointer callback_data)
{ {
MMPluginBaseSupportsTask *task; MMPluginBaseSupportsTask *task;
MMPluginBaseSupportsTaskPrivate *priv; MMPluginBaseSupportsTaskPrivate *priv;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), NULL);
g_return_val_if_fail (port != NULL, NULL);
g_return_val_if_fail (physdev_path != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (callback != NULL, NULL);
task = (MMPluginBaseSupportsTask *) g_object_new (MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, NULL); task = (MMPluginBaseSupportsTask *) g_object_new (MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, NULL);
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
@@ -210,8 +210,7 @@ supports_task_new (MMPluginBase *self,
priv->port = g_object_ref (port); priv->port = g_object_ref (port);
priv->physdev_path = g_strdup (physdev_path); priv->physdev_path = g_strdup (physdev_path);
priv->driver = g_strdup (driver); priv->driver = g_strdup (driver);
priv->callback = callback; priv->async_result = g_object_ref (result);
priv->callback_data = callback_data;
return task; return task;
} }
@@ -316,27 +315,24 @@ mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task,
guint32 level) guint32 level)
{ {
MMPluginBaseSupportsTaskPrivate *priv; MMPluginBaseSupportsTaskPrivate *priv;
const char *subsys, *name;
g_return_if_fail (task != NULL);
g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task)); g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task));
priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task);
g_return_if_fail (priv->callback != NULL);
if (priv->full_id) { if (priv->full_id) {
g_source_remove (priv->full_id); g_source_remove (priv->full_id);
priv->full_id = 0; priv->full_id = 0;
} }
subsys = g_udev_device_get_subsystem (priv->port); g_simple_async_result_set_op_res_gpointer (
name = g_udev_device_get_name (priv->port); priv->async_result,
supports_port_result_new ((level > 0 ?
priv->callback (MM_PLUGIN (priv->plugin), subsys, name, level, priv->callback_data); MM_PLUGIN_SUPPORTS_PORT_SUPPORTED :
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED),
/* Clear out the callback, it shouldn't be called more than once */ level),
priv->callback = NULL; (GDestroyNotify)supports_port_result_free);
priv->callback_data = NULL; g_simple_async_result_complete (priv->async_result);
} }
void void
@@ -1306,71 +1302,148 @@ device_file_exists(const char *name)
return (0 == result) ? TRUE : FALSE; return (0 == result) ? TRUE : FALSE;
} }
static MMPluginSupportsResult static SupportsPortResult *
supports_port (MMPlugin *plugin, supports_port_result_new (MMPluginSupportsResult result,
const char *subsys, guint support_level)
const char *name,
const char *physdev_path,
MMModem *existing,
MMSupportsPortResultFunc callback,
gpointer callback_data)
{ {
SupportsPortResult *spr;
spr = g_malloc (sizeof (SupportsPortResult));
spr->result = result;
spr->support_level = support_level;
return spr;
}
static void
supports_port_result_free (SupportsPortResult *spr)
{
g_free (spr);
}
static MMPluginSupportsResult
supports_port_finish (MMPlugin *self,
GAsyncResult *result,
guint *level,
GError **error)
{
SupportsPortResult *spr;
g_return_val_if_fail (MM_IS_PLUGIN (self),
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result),
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
/* Propagate error, if any */
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
error)) {
return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
}
spr = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
*level = spr->support_level;
return spr->result;
}
static void
supports_port (MMPlugin *plugin,
const gchar *subsys,
const gchar *name,
const gchar *physdev_path,
MMModem *existing,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
MMPluginBase *self = MM_PLUGIN_BASE (plugin); MMPluginBase *self = MM_PLUGIN_BASE (plugin);
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
GUdevDevice *port = NULL; GUdevDevice *port = NULL;
char *driver = NULL, *key = NULL; gchar *driver = NULL;
gchar *key = NULL;
MMPluginBaseSupportsTask *task; MMPluginBaseSupportsTask *task;
MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; gint idx;
int idx; GSimpleAsyncResult *async_result;
async_result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
supports_port);
/* Lookup task, there shouldn't be any */
key = get_key (subsys, name); key = get_key (subsys, name);
task = g_hash_table_lookup (priv->tasks, key); task = g_hash_table_lookup (priv->tasks, key);
if (task) { if (task) {
g_free (key); g_warn_if_reached ();
g_return_val_if_fail (task == NULL, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED); goto unsupported;
} }
port = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name); /* Get port device */
port = g_udev_client_query_by_subsystem_and_name (priv->client,
subsys,
name);
if (!port) if (!port)
goto out; goto unsupported;
// Detect any modems accessible through the list of virtual ports /* Detect any modems accessible through the list of virtual ports */
for (idx = 0; virtual_port[idx]; idx++) { for (idx = 0; virtual_port[idx]; idx++) {
if (strcmp(name, virtual_port[idx])) if (strcmp(name, virtual_port[idx]))
continue; continue;
if (!device_file_exists(virtual_port[idx])) if (!device_file_exists(virtual_port[idx]))
continue; continue;
task = supports_task_new (self, port, physdev_path, "virtual", callback, callback_data); task = supports_task_new (self,
g_assert (task); port,
g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task)); physdev_path,
"virtual",
async_result);
goto find_plugin; goto find_plugin;
} }
driver = get_driver_name (port); driver = get_driver_name (port);
if (!driver) if (!driver)
goto out; goto unsupported;
task = supports_task_new (self, port, physdev_path, driver, callback, callback_data); /* Create new supports task for this port */
g_assert (task); task = supports_task_new (self,
g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task)); port,
physdev_path,
driver,
async_result);
find_plugin: find_plugin:
result = MM_PLUGIN_BASE_GET_CLASS (self)->supports_port (self, existing, task); /* Keep track of the task */
if (result != MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS) { g_hash_table_insert (priv->tasks,
/* If the plugin doesn't support the port at all, the supports task is g_strdup (key),
* not needed. g_object_ref (task));
*/
g_hash_table_remove (priv->tasks, key); /* Let the specific plugin check port support */
result = MM_PLUGIN_BASE_GET_CLASS (self)->supports_port (self,
existing,
task);
if (result == MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS) {
/* It will really be asynchronous */
goto out;
} }
/* If the plugin doesn't support the port at all, the supports task is
* not needed.
*/
g_hash_table_remove (priv->tasks, key);
g_object_unref (task); g_object_unref (task);
unsupported:
/* Set the result of the asynchronous method just here */
g_simple_async_result_set_op_res_gpointer (
async_result,
supports_port_result_new (result, 0),
(GDestroyNotify)supports_port_result_free);
g_simple_async_result_complete_in_idle (async_result);
out: out:
if (port) if (port)
g_object_unref (port); g_object_unref (port);
g_free (key); g_free (key);
g_free (driver); g_free (driver);
return result; g_object_unref (async_result);
} }
static void static void
@@ -1434,6 +1507,7 @@ plugin_init (MMPlugin *plugin_class)
plugin_class->get_name = get_name; plugin_class->get_name = get_name;
plugin_class->get_sort_last = get_sort_last; plugin_class->get_sort_last = get_sort_last;
plugin_class->supports_port = supports_port; plugin_class->supports_port = supports_port;
plugin_class->supports_port_finish = supports_port_finish;
plugin_class->cancel_supports_port = cancel_supports_port; plugin_class->cancel_supports_port = cancel_supports_port;
plugin_class->grab_port = grab_port; plugin_class->grab_port = grab_port;
} }

View File

@@ -32,28 +32,45 @@ mm_plugin_get_sort_last (const MMPlugin *plugin)
return MM_PLUGIN_GET_INTERFACE (plugin)->get_sort_last (plugin); return MM_PLUGIN_GET_INTERFACE (plugin)->get_sort_last (plugin);
} }
MMPluginSupportsResult void
mm_plugin_supports_port (MMPlugin *plugin, mm_plugin_supports_port (MMPlugin *self,
const char *subsys, const gchar *subsys,
const char *name, const gchar *name,
const char *physdev_path, const gchar *physdev_path,
MMModem *existing, MMModem *existing,
MMSupportsPortResultFunc callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
{ {
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); g_return_if_fail (MM_IS_PLUGIN (self));
g_return_val_if_fail (subsys != NULL, FALSE); g_return_if_fail (subsys != NULL);
g_return_val_if_fail (name != NULL, FALSE); g_return_if_fail (name != NULL);
g_return_val_if_fail (physdev_path != NULL, FALSE); g_return_if_fail (physdev_path != NULL);
g_return_val_if_fail (callback != NULL, FALSE); g_return_if_fail (callback != NULL);
return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, MM_PLUGIN_GET_INTERFACE (self)->supports_port (self,
subsys, subsys,
name, name,
physdev_path, physdev_path,
existing, existing,
callback, callback,
user_data); user_data);
}
MMPluginSupportsResult
mm_plugin_supports_port_finish (MMPlugin *self,
GAsyncResult *result,
guint *level,
GError **error)
{
g_return_val_if_fail (MM_IS_PLUGIN (self),
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result),
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
return MM_PLUGIN_GET_INTERFACE (self)->supports_port_finish (self,
result,
level,
error);
} }
void void

View File

@@ -18,6 +18,8 @@
#define MM_PLUGIN_H #define MM_PLUGIN_H
#include <glib-object.h> #include <glib-object.h>
#include <gio/gio.h>
#include <mm-modem.h> #include <mm-modem.h>
#define MM_PLUGIN_GENERIC_NAME "Generic" #define MM_PLUGIN_GENERIC_NAME "Generic"
@@ -48,7 +50,8 @@ typedef void (*MMSupportsPortResultFunc) (MMPlugin *plugin,
typedef enum { typedef enum {
MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED = 0x0, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED = 0x0,
MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS, MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS,
MM_PLUGIN_SUPPORTS_PORT_DEFER MM_PLUGIN_SUPPORTS_PORT_DEFER,
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
} MMPluginSupportsResult; } MMPluginSupportsResult;
struct _MMPlugin { struct _MMPlugin {
@@ -63,22 +66,24 @@ struct _MMPlugin {
gboolean (*get_sort_last) (const MMPlugin *self); gboolean (*get_sort_last) (const MMPlugin *self);
/* Check whether a plugin supports a particular modem port, and what level /* Check whether a plugin supports a particular modem port, and what level
* of support the plugin has for the device. If the plugin can immediately * of support the plugin has for the device.
* determine whether a port is unsupported, it should return * The check is done always asynchronously. When the result of the check is
* FALSE. Otherwise, if the plugin needs to perform additional operations * ready, the passed callback will be called, and the result will be ready
* (ie, probing) to determine the level of support or additional details * to get retrieved with supports_port_finish().
* about a port, it should queue that operation (but *not* block on the
* result) and return TRUE to indicate the operation is ongoing. When the
* operation is finished or the level of support is known, the plugin should
* call the provided callback and pass that callback the provided user_data.
*/ */
MMPluginSupportsResult (*supports_port) (MMPlugin *self, void (* supports_port) (MMPlugin *self,
const char *subsys, const gchar *subsys,
const char *name, const gchar *name,
const char *physdev_path, const gchar *physdev_path,
MMModem *existing, MMModem *existing,
MMSupportsPortResultFunc callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
/* Allows to get the result of an asynchronous port support check. */
MMPluginSupportsResult (* supports_port_finish) (MMPlugin *self,
GAsyncResult *result,
guint *level,
GError **error);
/* Called to cancel an ongoing supports_port() operation or to notify the /* Called to cancel an ongoing supports_port() operation or to notify the
* plugin to clean up that operation. For example, if two plugins support * plugin to clean up that operation. For example, if two plugins support
@@ -111,13 +116,18 @@ const char *mm_plugin_get_name (MMPlugin *plugin);
gboolean mm_plugin_get_sort_last (const MMPlugin *plugin); gboolean mm_plugin_get_sort_last (const MMPlugin *plugin);
MMPluginSupportsResult mm_plugin_supports_port (MMPlugin *plugin, void mm_plugin_supports_port (MMPlugin *plugin,
const char *subsys, const gchar *subsys,
const char *name, const gchar *name,
const char *physdev_path, const gchar *physdev_path,
MMModem *existing, MMModem *existing,
MMSupportsPortResultFunc callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *plugin,
GAsyncResult *result,
guint *level,
GError **error);
void mm_plugin_cancel_supports_port (MMPlugin *plugin, void mm_plugin_cancel_supports_port (MMPlugin *plugin,
const char *subsys, const char *subsys,