plugin-manager,plugin: run pre-probing filters early

For each port, we will construct the list of plugins to test with. In that list
we will include those plugins which are likely to handle a given port, so we
will skip all those which aren't.

To see if a plugin is likely or not, we will run the pre-probing filters before
adding them to the list, with the new `mm_plugin_discard_port_early()'. This
method will return one of these hints:

 * UNSUPPORTED: The plugin will not be able to handle this port.
 * MAYBE: The plugin may handle this port.
 * LIKELY: The plugin may (very likely) handle this port.
 * SUPPORTED: If any plugin should support the port, this is it.

Plugins reported to be 'likely' supporting the port will be probed before the
ones reported just as 'maybe'.

If a plugin reports 'supported' only that one and the fallback generic ones will
be tried.
This commit is contained in:
Aleksander Morgado
2012-10-26 13:41:27 +02:00
parent e5b4b4d0e5
commit 0ca6ae1b4b
3 changed files with 152 additions and 64 deletions

View File

@@ -42,9 +42,12 @@ G_DEFINE_TYPE_EXTENDED (MMPluginManager, mm_plugin_manager, G_TYPE_OBJECT, 0,
initable_iface_init));
struct _MMPluginManagerPrivate {
/* The list of plugins. It is loaded once when the program starts, and the
* list is NOT expected to change after that. */
/* This list contains all plugins except for the generic one, order is not
* important. It is loaded once when the program starts, and the list is NOT
* expected to change after that.*/
GList *plugins;
/* Last, the generic plugin. */
MMPlugin *generic;
};
/*****************************************************************************/
@@ -65,6 +68,7 @@ typedef struct {
FindDeviceSupportContext *parent_ctx;
GUdevDevice *port;
GList *plugins;
GList *current;
MMPlugin *best_plugin;
MMPlugin *suggested_plugin;
@@ -86,6 +90,8 @@ port_probe_context_free (PortProbeContext *ctx)
g_object_unref (ctx->best_plugin);
if (ctx->suggested_plugin)
g_object_unref (ctx->suggested_plugin);
if (ctx->plugins)
g_list_free_full (ctx->plugins, (GDestroyNotify)g_object_unref);
g_object_unref (ctx->port);
g_slice_free (PortProbeContext, ctx);
}
@@ -426,6 +432,62 @@ port_probe_context_step (PortProbeContext *port_probe_ctx)
port_probe_ctx);
}
static GList *
build_plugins_list (MMPluginManager *self,
MMDevice *device,
GUdevDevice *port)
{
GList *list = NULL;
GList *l;
gboolean supported_found = FALSE;
for (l = self->priv->plugins; l && !supported_found; l = g_list_next (l)) {
MMPluginSupportsHint hint;
hint = mm_plugin_discard_port_early (MM_PLUGIN (l->data), device, port);
switch (hint) {
case MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED:
/* Fully discard */
break;
case MM_PLUGIN_SUPPORTS_HINT_MAYBE:
/* Maybe supported, add to tail of list */
list = g_list_append (list, g_object_ref (l->data));
break;
case MM_PLUGIN_SUPPORTS_HINT_LIKELY:
/* Likely supported, add to head of list */
list = g_list_prepend (list, g_object_ref (l->data));
break;
case MM_PLUGIN_SUPPORTS_HINT_SUPPORTED:
/* Really supported, clean existing list and add it alone */
if (list) {
g_list_free_full (list, (GDestroyNotify)g_object_unref);
list = NULL;
}
list = g_list_prepend (list, g_object_ref (l->data));
/* This will end the loop as well */
supported_found = TRUE;
break;
default:
g_assert_not_reached();
}
}
/* Add the generic plugin at the end of the list */
if (self->priv->generic)
list = g_list_append (list, g_object_ref (self->priv->generic));
mm_info ("(Plugin Manager) [%s] Found '%u' plugins to try...",
g_udev_device_get_name (port),
g_list_length (list));
for (l = list; l; l = g_list_next (l)) {
mm_info ("(Plugin Manager) [%s] Will try with plugin '%s'",
g_udev_device_get_name (port),
mm_plugin_get_name (MM_PLUGIN (l->data)));
}
return list;
}
static void
device_port_grabbed_cb (MMDevice *device,
GUdevDevice *port,
@@ -433,17 +495,15 @@ device_port_grabbed_cb (MMDevice *device,
{
PortProbeContext *port_probe_ctx;
mm_dbg ("(%s/%s) Launching port support check",
g_udev_device_get_subsystem (port),
g_udev_device_get_name (port));
/* Launch probing task on this port with the first plugin of the list */
port_probe_ctx = g_slice_new0 (PortProbeContext);
port_probe_ctx->parent_ctx = ctx;
port_probe_ctx->port = g_object_ref (port);
/* Set first plugin to check */
port_probe_ctx->current = ctx->self->priv->plugins;
/* Setup plugins to probe and first one to check */
port_probe_ctx->plugins = build_plugins_list (ctx->self, device, port);
port_probe_ctx->current = port_probe_ctx->plugins;
/* If we got one suggested, it will be the first one */
port_probe_ctx->suggested_plugin = (!!mm_device_peek_plugin (device) ?
@@ -585,9 +645,7 @@ load_plugins (MMPluginManager *self,
{
GDir *dir = NULL;
const gchar *fname;
MMPlugin *generic_plugin = NULL;
gchar *plugindir_display = NULL;
GList *l;
if (!g_module_supported ()) {
g_set_error (error,
@@ -622,27 +680,25 @@ load_plugins (MMPluginManager *self,
plugin = load_plugin (path);
g_free (path);
if (plugin) {
if (g_str_equal (mm_plugin_get_name (plugin),
MM_PLUGIN_GENERIC_NAME))
generic_plugin = plugin;
else
self->priv->plugins = g_list_append (self->priv->plugins,
plugin);
}
if (!plugin)
continue;
mm_info ("Loaded plugin '%s'", mm_plugin_get_name (plugin));
if (g_str_equal (mm_plugin_get_name (plugin), MM_PLUGIN_GENERIC_NAME))
/* Generic plugin */
self->priv->generic = plugin;
else
/* Vendor specific plugin */
self->priv->plugins = g_list_append (self->priv->plugins, plugin);
}
/* Sort last plugins that request it */
self->priv->plugins = g_list_sort (self->priv->plugins,
(GCompareFunc)mm_plugin_cmp);
/* Make sure the generic plugin is last */
if (generic_plugin)
self->priv->plugins = g_list_append (self->priv->plugins,
generic_plugin);
/* Check the generic plugin once all looped */
if (!self->priv->generic)
mm_warn ("Generic plugin not loaded");
/* Treat as error if we don't find any plugin */
if (!self->priv->plugins) {
if (!self->priv->plugins && !self->priv->generic) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_NO_PLUGINS,
@@ -651,20 +707,16 @@ load_plugins (MMPluginManager *self,
goto out;
}
/* Now report about all the found plugins, in the same order they will be
* used while checking support */
for (l = self->priv->plugins; l; l = g_list_next (l))
mm_info ("Loaded plugin '%s'", mm_plugin_get_name (MM_PLUGIN (l->data)));
mm_info ("Successfully loaded %u plugins",
g_list_length (self->priv->plugins));
g_list_length (self->priv->plugins) + !!self->priv->generic);
out:
if (dir)
g_dir_close (dir);
g_free (plugindir_display);
return !!self->priv->plugins;
/* Return TRUE if at least one plugin found */
return (self->priv->plugins || self->priv->generic);
}
MMPluginManager *
@@ -704,6 +756,7 @@ dispose (GObject *object)
g_list_free_full (self->priv->plugins, (GDestroyNotify)g_object_unref);
self->priv->plugins = NULL;
}
g_clear_object (&self->priv->generic);
G_OBJECT_CLASS (mm_plugin_manager_parent_class)->dispose (object);
}

View File

@@ -43,31 +43,48 @@ G_DEFINE_TYPE (MMPlugin, mm_plugin, G_TYPE_OBJECT)
/* Virtual port corresponding to the embeded modem */
static gchar *virtual_port[] = {"smd0", NULL};
#define HAS_POST_PROBING_FILTERS(self) \
(self->priv->vendor_strings || \
self->priv->product_strings || \
self->priv->forbidden_product_strings || \
self->priv->allowed_icera || \
self->priv->forbidden_icera || \
self->priv->custom_init)
struct _MMPluginPrivate {
gchar *name;
GHashTable *tasks;
/* Plugin-specific setups */
/* Pre-probing filters */
gchar **subsystems;
gchar **drivers;
gchar **forbidden_drivers;
guint16 *vendor_ids;
mm_uint16_pair *product_ids;
mm_uint16_pair *forbidden_product_ids;
gchar **udev_tags;
/* Post probing filters */
gchar **vendor_strings;
mm_str_pair *product_strings;
mm_str_pair *forbidden_product_strings;
gchar **udev_tags;
gboolean allowed_icera;
gboolean forbidden_icera;
/* Probing setup */
gboolean at;
gboolean single_at;
gboolean qcdm;
gboolean icera_probe;
gboolean allowed_icera;
gboolean forbidden_icera;
MMPortProbeAtCommand *custom_at_probe;
MMAsyncMethod *custom_init;
guint64 send_delay;
gboolean remove_echo;
/* Probing setup and/or post-probing filter.
* Plugins may use this method to decide whether they support a given
* port or not, so should also be considered kind of post-probing filter. */
MMAsyncMethod *custom_init;
};
enum {
@@ -106,30 +123,6 @@ mm_plugin_get_name (MMPlugin *self)
/*****************************************************************************/
gint
mm_plugin_cmp (const MMPlugin *plugin_a,
const MMPlugin *plugin_b)
{
/* If we have any post-probing filter, we need to sort the plugin last */
#define SORT_LAST(self) (self->priv->vendor_strings || \
self->priv->product_strings || \
self->priv->forbidden_product_strings)
/* The order of the plugins in the list is the same order used to check
* whether the plugin can manage a given modem:
* - First, modems that will check vendor ID from udev.
* - Then, modems that report to be sorted last (those which will check
* vendor ID also from the probed ones..
*/
if (SORT_LAST (plugin_a) && !SORT_LAST (plugin_b))
return 1;
if (!SORT_LAST (plugin_a) && SORT_LAST (plugin_b))
return -1;
return 0;
}
/*****************************************************************************/
static gboolean
device_file_exists (const char *name)
{
@@ -747,6 +740,37 @@ out:
/*****************************************************************************/
MMPluginSupportsHint
mm_plugin_discard_port_early (MMPlugin *self,
MMDevice *device,
GUdevDevice *port)
{
gboolean need_vendor_probing = FALSE;
gboolean need_product_probing = FALSE;
/* If fully filtered by pre-probing filters, port unsupported */
if (apply_pre_probing_filters (self,
device,
port,
&need_vendor_probing,
&need_product_probing))
return MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED;
/* If there are no post-probing filters, this plugin is the only one (except
* for the generic one) which will grab the port */
if (!HAS_POST_PROBING_FILTERS (self))
return MM_PLUGIN_SUPPORTS_HINT_SUPPORTED;
/* If no vendor/product probing needed, plugin is likely supported */
if (!need_vendor_probing && !need_product_probing)
return MM_PLUGIN_SUPPORTS_HINT_LIKELY;
/* If vendor/product probing is needed, plugin may be supported */
return MM_PLUGIN_SUPPORTS_HINT_MAYBE;
}
/*****************************************************************************/
MMBaseModem *
mm_plugin_create_modem (MMPlugin *self,
MMDevice *device,

View File

@@ -67,6 +67,13 @@ typedef enum {
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
} MMPluginSupportsResult;
typedef enum {
MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED,
MM_PLUGIN_SUPPORTS_HINT_MAYBE,
MM_PLUGIN_SUPPORTS_HINT_LIKELY,
MM_PLUGIN_SUPPORTS_HINT_SUPPORTED,
} MMPluginSupportsHint;
typedef struct _MMPlugin MMPlugin;
typedef struct _MMPluginClass MMPluginClass;
typedef struct _MMPluginPrivate MMPluginPrivate;
@@ -102,8 +109,12 @@ struct _MMPluginClass {
GType mm_plugin_get_type (void);
const gchar *mm_plugin_get_name (MMPlugin *plugin);
gint mm_plugin_cmp (const MMPlugin *plugin_a,
const MMPlugin *plugin_b);
/* This method will run all pre-probing filters, to see if we can discard this
* plugin from the probing logic as soon as possible. */
MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *plugin,
MMDevice *device,
GUdevDevice *port);
void mm_plugin_supports_port (MMPlugin *plugin,
MMDevice *device,