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));
|
initable_iface_init));
|
||||||
|
|
||||||
struct _MMPluginManagerPrivate {
|
struct _MMPluginManagerPrivate {
|
||||||
/* The list of plugins. It is loaded once when the program starts, and the
|
/* This list contains all plugins except for the generic one, order is not
|
||||||
* list is NOT expected to change after that. */
|
* important. It is loaded once when the program starts, and the list is NOT
|
||||||
|
* expected to change after that.*/
|
||||||
GList *plugins;
|
GList *plugins;
|
||||||
|
/* Last, the generic plugin. */
|
||||||
|
MMPlugin *generic;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -65,6 +68,7 @@ typedef struct {
|
|||||||
FindDeviceSupportContext *parent_ctx;
|
FindDeviceSupportContext *parent_ctx;
|
||||||
GUdevDevice *port;
|
GUdevDevice *port;
|
||||||
|
|
||||||
|
GList *plugins;
|
||||||
GList *current;
|
GList *current;
|
||||||
MMPlugin *best_plugin;
|
MMPlugin *best_plugin;
|
||||||
MMPlugin *suggested_plugin;
|
MMPlugin *suggested_plugin;
|
||||||
@@ -86,6 +90,8 @@ port_probe_context_free (PortProbeContext *ctx)
|
|||||||
g_object_unref (ctx->best_plugin);
|
g_object_unref (ctx->best_plugin);
|
||||||
if (ctx->suggested_plugin)
|
if (ctx->suggested_plugin)
|
||||||
g_object_unref (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_object_unref (ctx->port);
|
||||||
g_slice_free (PortProbeContext, ctx);
|
g_slice_free (PortProbeContext, ctx);
|
||||||
}
|
}
|
||||||
@@ -426,6 +432,62 @@ port_probe_context_step (PortProbeContext *port_probe_ctx)
|
|||||||
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
|
static void
|
||||||
device_port_grabbed_cb (MMDevice *device,
|
device_port_grabbed_cb (MMDevice *device,
|
||||||
GUdevDevice *port,
|
GUdevDevice *port,
|
||||||
@@ -433,17 +495,15 @@ device_port_grabbed_cb (MMDevice *device,
|
|||||||
{
|
{
|
||||||
PortProbeContext *port_probe_ctx;
|
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 */
|
/* Launch probing task on this port with the first plugin of the list */
|
||||||
port_probe_ctx = g_slice_new0 (PortProbeContext);
|
port_probe_ctx = g_slice_new0 (PortProbeContext);
|
||||||
port_probe_ctx->parent_ctx = ctx;
|
port_probe_ctx->parent_ctx = ctx;
|
||||||
port_probe_ctx->port = g_object_ref (port);
|
port_probe_ctx->port = g_object_ref (port);
|
||||||
|
|
||||||
/* Set first plugin to check */
|
/* Setup plugins to probe and first one to check */
|
||||||
port_probe_ctx->current = ctx->self->priv->plugins;
|
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 */
|
/* If we got one suggested, it will be the first one */
|
||||||
port_probe_ctx->suggested_plugin = (!!mm_device_peek_plugin (device) ?
|
port_probe_ctx->suggested_plugin = (!!mm_device_peek_plugin (device) ?
|
||||||
@@ -585,9 +645,7 @@ load_plugins (MMPluginManager *self,
|
|||||||
{
|
{
|
||||||
GDir *dir = NULL;
|
GDir *dir = NULL;
|
||||||
const gchar *fname;
|
const gchar *fname;
|
||||||
MMPlugin *generic_plugin = NULL;
|
|
||||||
gchar *plugindir_display = NULL;
|
gchar *plugindir_display = NULL;
|
||||||
GList *l;
|
|
||||||
|
|
||||||
if (!g_module_supported ()) {
|
if (!g_module_supported ()) {
|
||||||
g_set_error (error,
|
g_set_error (error,
|
||||||
@@ -622,27 +680,25 @@ load_plugins (MMPluginManager *self,
|
|||||||
plugin = load_plugin (path);
|
plugin = load_plugin (path);
|
||||||
g_free (path);
|
g_free (path);
|
||||||
|
|
||||||
if (plugin) {
|
if (!plugin)
|
||||||
if (g_str_equal (mm_plugin_get_name (plugin),
|
continue;
|
||||||
MM_PLUGIN_GENERIC_NAME))
|
|
||||||
generic_plugin = plugin;
|
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
|
else
|
||||||
self->priv->plugins = g_list_append (self->priv->plugins,
|
/* Vendor specific plugin */
|
||||||
plugin);
|
self->priv->plugins = g_list_append (self->priv->plugins, plugin);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort last plugins that request it */
|
/* Check the generic plugin once all looped */
|
||||||
self->priv->plugins = g_list_sort (self->priv->plugins,
|
if (!self->priv->generic)
|
||||||
(GCompareFunc)mm_plugin_cmp);
|
mm_warn ("Generic plugin not loaded");
|
||||||
|
|
||||||
/* Make sure the generic plugin is last */
|
|
||||||
if (generic_plugin)
|
|
||||||
self->priv->plugins = g_list_append (self->priv->plugins,
|
|
||||||
generic_plugin);
|
|
||||||
|
|
||||||
/* Treat as error if we don't find any plugin */
|
/* 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,
|
g_set_error (error,
|
||||||
MM_CORE_ERROR,
|
MM_CORE_ERROR,
|
||||||
MM_CORE_ERROR_NO_PLUGINS,
|
MM_CORE_ERROR_NO_PLUGINS,
|
||||||
@@ -651,20 +707,16 @@ load_plugins (MMPluginManager *self,
|
|||||||
goto out;
|
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",
|
mm_info ("Successfully loaded %u plugins",
|
||||||
g_list_length (self->priv->plugins));
|
g_list_length (self->priv->plugins) + !!self->priv->generic);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (dir)
|
if (dir)
|
||||||
g_dir_close (dir);
|
g_dir_close (dir);
|
||||||
g_free (plugindir_display);
|
g_free (plugindir_display);
|
||||||
|
|
||||||
return !!self->priv->plugins;
|
/* Return TRUE if at least one plugin found */
|
||||||
|
return (self->priv->plugins || self->priv->generic);
|
||||||
}
|
}
|
||||||
|
|
||||||
MMPluginManager *
|
MMPluginManager *
|
||||||
@@ -704,6 +756,7 @@ dispose (GObject *object)
|
|||||||
g_list_free_full (self->priv->plugins, (GDestroyNotify)g_object_unref);
|
g_list_free_full (self->priv->plugins, (GDestroyNotify)g_object_unref);
|
||||||
self->priv->plugins = NULL;
|
self->priv->plugins = NULL;
|
||||||
}
|
}
|
||||||
|
g_clear_object (&self->priv->generic);
|
||||||
|
|
||||||
G_OBJECT_CLASS (mm_plugin_manager_parent_class)->dispose (object);
|
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 */
|
/* Virtual port corresponding to the embeded modem */
|
||||||
static gchar *virtual_port[] = {"smd0", NULL};
|
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 {
|
struct _MMPluginPrivate {
|
||||||
gchar *name;
|
gchar *name;
|
||||||
GHashTable *tasks;
|
GHashTable *tasks;
|
||||||
|
|
||||||
/* Plugin-specific setups */
|
/* Pre-probing filters */
|
||||||
gchar **subsystems;
|
gchar **subsystems;
|
||||||
gchar **drivers;
|
gchar **drivers;
|
||||||
gchar **forbidden_drivers;
|
gchar **forbidden_drivers;
|
||||||
guint16 *vendor_ids;
|
guint16 *vendor_ids;
|
||||||
mm_uint16_pair *product_ids;
|
mm_uint16_pair *product_ids;
|
||||||
mm_uint16_pair *forbidden_product_ids;
|
mm_uint16_pair *forbidden_product_ids;
|
||||||
|
gchar **udev_tags;
|
||||||
|
|
||||||
|
/* Post probing filters */
|
||||||
gchar **vendor_strings;
|
gchar **vendor_strings;
|
||||||
mm_str_pair *product_strings;
|
mm_str_pair *product_strings;
|
||||||
mm_str_pair *forbidden_product_strings;
|
mm_str_pair *forbidden_product_strings;
|
||||||
gchar **udev_tags;
|
gboolean allowed_icera;
|
||||||
|
gboolean forbidden_icera;
|
||||||
|
|
||||||
|
/* Probing setup */
|
||||||
gboolean at;
|
gboolean at;
|
||||||
gboolean single_at;
|
gboolean single_at;
|
||||||
gboolean qcdm;
|
gboolean qcdm;
|
||||||
gboolean icera_probe;
|
gboolean icera_probe;
|
||||||
gboolean allowed_icera;
|
|
||||||
gboolean forbidden_icera;
|
|
||||||
MMPortProbeAtCommand *custom_at_probe;
|
MMPortProbeAtCommand *custom_at_probe;
|
||||||
MMAsyncMethod *custom_init;
|
|
||||||
guint64 send_delay;
|
guint64 send_delay;
|
||||||
gboolean remove_echo;
|
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 {
|
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
|
static gboolean
|
||||||
device_file_exists (const char *name)
|
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 *
|
MMBaseModem *
|
||||||
mm_plugin_create_modem (MMPlugin *self,
|
mm_plugin_create_modem (MMPlugin *self,
|
||||||
MMDevice *device,
|
MMDevice *device,
|
||||||
|
@@ -67,6 +67,13 @@ typedef enum {
|
|||||||
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
|
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
|
||||||
} MMPluginSupportsResult;
|
} 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 _MMPlugin MMPlugin;
|
||||||
typedef struct _MMPluginClass MMPluginClass;
|
typedef struct _MMPluginClass MMPluginClass;
|
||||||
typedef struct _MMPluginPrivate MMPluginPrivate;
|
typedef struct _MMPluginPrivate MMPluginPrivate;
|
||||||
@@ -102,8 +109,12 @@ struct _MMPluginClass {
|
|||||||
GType mm_plugin_get_type (void);
|
GType mm_plugin_get_type (void);
|
||||||
|
|
||||||
const gchar *mm_plugin_get_name (MMPlugin *plugin);
|
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,
|
void mm_plugin_supports_port (MMPlugin *plugin,
|
||||||
MMDevice *device,
|
MMDevice *device,
|
||||||
|
Reference in New Issue
Block a user