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:
@@ -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);
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user