diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-generic-cdma.c | 1 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 1 | ||||
-rw-r--r-- | src/mm-manager.c | 313 | ||||
-rw-r--r-- | src/mm-modem-base.c | 2 | ||||
-rw-r--r-- | src/mm-modem.c | 3 | ||||
-rw-r--r-- | src/mm-modem.h | 2 | ||||
-rw-r--r-- | src/mm-plugin-base.c | 796 | ||||
-rw-r--r-- | src/mm-plugin-base.h | 89 | ||||
-rw-r--r-- | src/mm-plugin.c | 21 | ||||
-rw-r--r-- | src/mm-plugin.h | 61 | ||||
-rw-r--r-- | src/mm-serial-parsers.c | 8 | ||||
-rw-r--r-- | src/mm-serial-port.c | 1 |
12 files changed, 1181 insertions, 117 deletions
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 9dad455f..a333f2f2 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -85,6 +85,7 @@ static gboolean grab_port (MMModem *modem, const char *subsys, const char *name, + gpointer user_data, GError **error) { MMGenericCdma *self = MM_GENERIC_CDMA (modem); diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 73a27e5c..e27ab51d 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -252,6 +252,7 @@ static gboolean grab_port (MMModem *modem, const char *subsys, const char *name, + gpointer user_data, GError **error) { MMGenericGsm *self = MM_GENERIC_GSM (modem); diff --git a/src/mm-manager.c b/src/mm-manager.c index 3d3b1ac1..c6445cf6 100644 --- a/src/mm-manager.c +++ b/src/mm-manager.c @@ -36,6 +36,8 @@ typedef struct { GUdevClient *udev; GSList *plugins; GHashTable *modems; + + GHashTable *supports; } MMManagerPrivate; static MMPlugin * @@ -99,6 +101,7 @@ load_plugins (MMManager *manager) MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); GDir *dir; const char *fname; + MMPlugin *generic_plugin = NULL; if (!g_module_supported ()) { g_warning ("GModules are not supported on your platform!"); @@ -122,10 +125,18 @@ load_plugins (MMManager *manager) plugin = load_plugin (path); g_free (path); - if (plugin) - priv->plugins = g_slist_append (priv->plugins, plugin); + if (plugin) { + if (!strcmp (mm_plugin_get_name (plugin), MM_PLUGIN_GENERIC_NAME)) + generic_plugin = plugin; + else + priv->plugins = g_slist_append (priv->plugins, plugin); + } } + /* Make sure the generic plugin is last */ + if (generic_plugin) + priv->plugins = g_slist_append (priv->plugins, generic_plugin); + g_dir_close (dir); } @@ -179,7 +190,7 @@ modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data) device = mm_modem_get_device (modem); g_assert (device); - g_debug ("Exported modem %s", device); + g_debug ("Exported modem %s as %s", device, path); g_free (device); g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem); @@ -236,107 +247,281 @@ impl_manager_enumerate_devices (MMManager *manager, return TRUE; } +static MMModem * +find_modem_for_port (MMManager *manager, const char *subsys, const char *name) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->modems); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MMModem *modem = MM_MODEM (value); + + if (mm_modem_owns_port (modem, subsys, name)) + return modem; + } + return NULL; +} + typedef struct { - MMModem *modem; - const char *subsys; - const char *name; -} FindPortInfo; + MMManager *manager; + char *subsys; + char *name; + GSList *plugins; + GSList *cur_plugin; + guint defer_id; + + guint32 best_level; + MMPlugin *best_plugin; +} SupportsInfo; + +static SupportsInfo * +supports_info_new (MMManager *self, const char *subsys, const char *name) +{ + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self); + SupportsInfo *info; + + info = g_malloc0 (sizeof (SupportsInfo)); + info->manager = self; + info->subsys = g_strdup (subsys); + info->name = g_strdup (name); + info->plugins = g_slist_copy (priv->plugins); + info->cur_plugin = info->plugins; + return info; +} static void -find_port (gpointer key, gpointer data, gpointer user_data) +supports_info_free (SupportsInfo *info) { - FindPortInfo *info = user_data; - MMModem *modem = MM_MODEM (data); + /* Cancel any in-process operation on the first plugin */ + if (info->cur_plugin) + mm_plugin_cancel_supports_port (MM_PLUGIN (info->cur_plugin->data), info->subsys, info->name); + + if (info->defer_id) + g_source_remove (info->defer_id); + + g_free (info->subsys); + g_free (info->name); + g_slist_free (info->plugins); + memset (info, 0, sizeof (SupportsInfo)); + g_free (info); +} - if (!info->modem && mm_modem_owns_port (modem, info->subsys, info->name)) - info->modem = modem; +static char * +get_key (const char *subsys, const char *name) +{ + return g_strdup_printf ("%s%s", subsys, name); } -static MMModem * -find_modem_for_port (MMManager *manager, const char *subsys, const char *name) + +static void supports_callback (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data); + +static void try_supports_port (MMManager *manager, + MMPlugin *plugin, + const char *subsys, + const char *name, + SupportsInfo *info); + +static gboolean +supports_defer_timeout (gpointer user_data) { - MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - FindPortInfo info = { NULL, subsys, name }; + SupportsInfo *info = user_data; + + g_debug ("(%s): re-checking support...", info->name); + try_supports_port (info->manager, + MM_PLUGIN (info->cur_plugin->data), + info->subsys, + info->name, + info); + return FALSE; +} + +static void +try_supports_port (MMManager *manager, + MMPlugin *plugin, + const char *subsys, + const char *name, + SupportsInfo *info) +{ + MMPluginSupportsResult result; + + result = mm_plugin_supports_port (plugin, subsys, name, supports_callback, info); + + switch (result) { + case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED: + /* If the plugin knows it doesn't support the modem, just call the + * callback and indicate 0 support. + */ + supports_callback (plugin, subsys, name, 0, info); + break; + case MM_PLUGIN_SUPPORTS_PORT_DEFER: + g_debug ("(%s): (%s) deferring support check", mm_plugin_get_name (plugin), name); + if (info->defer_id) + g_source_remove (info->defer_id); + + /* defer port detection for a bit as requested by the plugin */ + info->defer_id = g_timeout_add (3000, supports_defer_timeout, info); + break; + case MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS: + default: + break; + } +} + +static void +supports_callback (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data) +{ + SupportsInfo *info = user_data; + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager); + MMModem *modem; + GError *error = NULL; + char *key; + GSList *iter; + MMPlugin *next_plugin = NULL; + + info->cur_plugin = info->cur_plugin->next; + if (info->cur_plugin) + next_plugin = MM_PLUGIN (info->cur_plugin->data); + + /* Is this plugin's result better than any one we've tried before? */ + if (level > info->best_level) { + info->best_level = level; + info->best_plugin = plugin; + } - g_hash_table_foreach (priv->modems, find_port, &info); - return info.modem; + /* Prevent the generic plugin from probing devices that are already supported + * by other plugins. For Huawei for example, secondary ports shouldn't + * be probed, but the generic plugin would happily do so if allowed to. + */ + if ( next_plugin + && !strcmp (mm_plugin_get_name (next_plugin), MM_PLUGIN_GENERIC_NAME) + && info->best_plugin) + next_plugin = NULL; + + /* Try the next plugin */ + if (next_plugin) { + try_supports_port (info->manager, next_plugin, info->subsys, info->name, info); + return; + } + + /* No more plugins to try */ + if (info->best_plugin) { + /* Create the modem */ + modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, &error); + if (modem) { + guint32 modem_type = MM_MODEM_TYPE_UNKNOWN; + const char *type_name = "UNKNOWN"; + + g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL); + if (modem_type == MM_MODEM_TYPE_GSM) + type_name = "GSM"; + else if (modem_type == MM_MODEM_TYPE_CDMA) + type_name = "CDMA"; + + g_message ("(%s): %s modem %s claimed port %s", + mm_plugin_get_name (info->best_plugin), + type_name, + mm_modem_get_device (modem), + name); + + add_modem (info->manager, modem); + } else { + g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", + __func__, + mm_plugin_get_name (info->best_plugin), + subsys, + name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + } + } + + /* Tell each plugin to clean up any outstanding supports task */ + for (iter = info->plugins; iter; iter = g_slist_next (iter)) + mm_plugin_cancel_supports_port (MM_PLUGIN (iter->data), subsys, name); + g_slist_free (info->plugins); + info->cur_plugin = info->plugins = NULL; + + key = get_key (info->subsys, info->name); + g_hash_table_remove (priv->supports, key); + g_free (key); } static void device_added (MMManager *manager, GUdevDevice *device) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - GSList *iter; - MMModem *modem = NULL; const char *subsys, *name; - MMPlugin *best_plugin = NULL; - guint32 best_level = 0; - GError *error = NULL; + SupportsInfo *info; + char *key; + gboolean found; g_return_if_fail (device != NULL); + if (!g_slist_length (priv->plugins)) + return; + subsys = g_udev_device_get_subsystem (device); name = g_udev_device_get_name (device); if (find_modem_for_port (manager, subsys, name)) return; - /* Build up the list of plugins that support this port */ - for (iter = priv->plugins; iter; iter = iter->next) { - MMPlugin *plugin = MM_PLUGIN (iter->data); - guint32 level; - - level = mm_plugin_supports_port (plugin, subsys, name); - if (level > best_level) { - best_plugin = plugin; - best_level = level; - } - } - - /* Let the best plugin handle this port */ - if (!best_plugin) - return; - - modem = mm_plugin_grab_port (best_plugin, subsys, name, &error); - if (modem) { - guint32 modem_type = MM_MODEM_TYPE_UNKNOWN; - const char *type_name = "UNKNOWN"; - - g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL); - if (modem_type == MM_MODEM_TYPE_GSM) - type_name = "GSM"; - else if (modem_type == MM_MODEM_TYPE_CDMA) - type_name = "CDMA"; - - g_message ("(%s): %s modem %s claimed port %s", - mm_plugin_get_name (best_plugin), - type_name, - mm_modem_get_device (modem), - name); - } else { - g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", - __func__, mm_plugin_get_name (best_plugin), subsys, name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); + key = get_key (subsys, name); + found = !!g_hash_table_lookup (priv->supports, key); + if (found) { + g_free (key); return; } - add_modem (manager, modem); + info = supports_info_new (manager, subsys, name); + g_hash_table_insert (priv->supports, key, info); + + try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info); } static void device_removed (MMManager *manager, GUdevDevice *device) { + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); MMModem *modem; const char *subsys, *name; + char *key; + SupportsInfo *info; g_return_if_fail (device != NULL); + if (!g_slist_length (priv->plugins)) + return; + subsys = g_udev_device_get_subsystem (device); name = g_udev_device_get_name (device); modem = find_modem_for_port (manager, subsys, name); - if (modem) + if (modem) { mm_modem_release_port (modem, subsys, name); + return; + } + + /* Maybe a plugin is checking whether or not the port is supported */ + key = get_key (subsys, name); + info = g_hash_table_lookup (priv->supports, key); + + if (info) { + if (info->plugins) + mm_plugin_cancel_supports_port (MM_PLUGIN (info->plugins->data), subsys, name); + g_hash_table_remove (priv->supports, key); + } + + g_free (key); } static void @@ -391,6 +576,8 @@ mm_manager_init (MMManager *manager) priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); load_plugins (manager); + priv->supports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) supports_info_free); + priv->udev = g_udev_client_new (subsys); g_assert (priv->udev); g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), manager); @@ -401,6 +588,7 @@ finalize (GObject *object) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (object); + g_hash_table_destroy (priv->supports); g_hash_table_destroy (priv->modems); g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); @@ -454,3 +642,4 @@ mm_manager_class_init (MMManagerClass *manager_class) dbus_g_error_domain_register (MM_MODEM_CONNECT_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_MODEM_CONNECT_ERROR); dbus_g_error_domain_register (MM_MOBILE_ERROR, "org.freedesktop.ModemManager.Modem.Gsm", MM_TYPE_MOBILE_ERROR); } + diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c index 1e719e58..30de1123 100644 --- a/src/mm-modem-base.c +++ b/src/mm-modem-base.c @@ -56,7 +56,7 @@ mm_modem_base_get_port (MMModemBase *self, key = get_hash_key (subsys, name); port = g_hash_table_lookup (MM_MODEM_BASE_GET_PRIVATE (self)->ports, key); g_free (key); - return NULL; + return port; } static void diff --git a/src/mm-modem.c b/src/mm-modem.c index 63a8929e..3a566d08 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -300,6 +300,7 @@ gboolean mm_modem_grab_port (MMModem *self, const char *subsys, const char *name, + gpointer user_data, GError **error) { g_return_val_if_fail (self != NULL, FALSE); @@ -308,7 +309,7 @@ mm_modem_grab_port (MMModem *self, g_return_val_if_fail (name, FALSE); g_assert (MM_MODEM_GET_INTERFACE (self)->grab_port); - return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, error); + return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, user_data, error); } void diff --git a/src/mm-modem.h b/src/mm-modem.h index 5a2c3a8a..9222b703 100644 --- a/src/mm-modem.h +++ b/src/mm-modem.h @@ -78,6 +78,7 @@ struct _MMModem { gboolean (*grab_port) (MMModem *self, const char *subsys, const char *name, + gpointer user_data, GError **error); void (*release_port) (MMModem *self, @@ -116,6 +117,7 @@ gboolean mm_modem_owns_port (MMModem *self, gboolean mm_modem_grab_port (MMModem *self, const char *subsys, const char *name, + gpointer user_data, GError **error); void mm_modem_release_port (MMModem *self, diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c index 6daa5895..25456ca5 100644 --- a/src/mm-plugin-base.c +++ b/src/mm-plugin-base.c @@ -23,66 +23,529 @@ #include <gudev/gudev.h> #include "mm-plugin-base.h" +#include "mm-serial-port.h" +#include "mm-serial-parsers.h" +#include "mm-errors.h" +#include "mm-marshal.h" -G_DEFINE_TYPE (MMPluginBase, mm_plugin_base, G_TYPE_OBJECT) +static void plugin_init (MMPlugin *plugin_class); + +G_DEFINE_TYPE_EXTENDED (MMPluginBase, mm_plugin_base, G_TYPE_OBJECT, + 0, G_IMPLEMENT_INTERFACE (MM_TYPE_PLUGIN, plugin_init)) #define MM_PLUGIN_BASE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE, MMPluginBasePrivate)) +/* A hash table shared between all instances of the plugin base that + * caches the probed capabilities so that only one plugin has to actually + * probe a port. + */ +static GHashTable *cached_caps = NULL; + + typedef struct { + char *name; + GUdevClient *client; + GHashTable *modems; + GHashTable *tasks; } MMPluginBasePrivate; +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +enum { + PROBE_RESULT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +typedef enum { + PROBE_STATE_GCAP_TRY1 = 0, + PROBE_STATE_GCAP_TRY2, + PROBE_STATE_GCAP_TRY3, + PROBE_STATE_ATI, + PROBE_STATE_CGMM, +} ProbeState; + +/*****************************************************************************/ + +G_DEFINE_TYPE (MMPluginBaseSupportsTask, mm_plugin_base_supports_task, G_TYPE_OBJECT) + +#define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskPrivate)) typedef struct { - char *key; - gpointer modem; -} FindInfo; + MMPluginBase *plugin; + GUdevDevice *port; + GUdevDevice *physdev; + char *driver; + + MMSerialPort *probe_port; + guint32 probed_caps; + ProbeState probe_state; + guint probe_id; + char *probe_resp; + GError *probe_error; + + MMSupportsPortResultFunc callback; + gpointer callback_data; +} MMPluginBaseSupportsTaskPrivate; + +static MMPluginBaseSupportsTask * +supports_task_new (MMPluginBase *self, + GUdevDevice *port, + GUdevDevice *physdev, + const char *driver, + MMSupportsPortResultFunc callback, + gpointer callback_data) +{ + MMPluginBaseSupportsTask *task; + 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 != 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); + + priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + priv->plugin = self; + priv->port = g_object_ref (port); + priv->physdev = g_object_ref (physdev); + priv->driver = g_strdup (driver); + priv->callback = callback; + priv->callback_data = callback_data; + + return task; +} + +MMPlugin * +mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN (MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->plugin); +} + +GUdevDevice * +mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->port; +} + +GUdevDevice * +mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->physdev; +} + +const char * +mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, NULL); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), NULL); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->driver; +} + +guint32 +mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *task) +{ + g_return_val_if_fail (task != NULL, 0); + g_return_val_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task), 0); + + return MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task)->probed_caps; +} + +void +mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, + guint32 level) +{ + MMPluginBaseSupportsTaskPrivate *priv; + const char *subsys, *name; + + g_return_if_fail (task != NULL); + g_return_if_fail (MM_IS_PLUGIN_BASE_SUPPORTS_TASK (task)); + + priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + g_return_if_fail (priv->callback != NULL); + + subsys = g_udev_device_get_subsystem (priv->port); + name = g_udev_device_get_name (priv->port); + + priv->callback (MM_PLUGIN (priv->plugin), subsys, name, level, priv->callback_data); + + /* Clear out the callback, it shouldn't be called more than once */ + priv->callback = NULL; + priv->callback_data = NULL; +} + +static void +mm_plugin_base_supports_task_init (MMPluginBaseSupportsTask *self) +{ +} static void -find_modem (gpointer key, gpointer data, gpointer user_data) +dispose (GObject *object) { - FindInfo *info = user_data; + MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object); + + g_object_unref (priv->port); + g_object_unref (priv->physdev); + g_free (priv->driver); + g_free (priv->probe_resp); + g_clear_error (&(priv->probe_error)); - if (!info->key && data == info->modem) - info->key = g_strdup ((const char *) key); + if (priv->probe_id) + g_source_remove (priv->probe_id); + if (priv->probe_port) + g_object_unref (priv->probe_port); + + G_OBJECT_CLASS (mm_plugin_base_supports_task_parent_class)->dispose (object); } static void -modem_destroyed (gpointer data, GObject *modem) +mm_plugin_base_supports_task_class_init (MMPluginBaseSupportsTaskClass *klass) { - MMPluginBase *self = MM_PLUGIN_BASE (data); - MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); - FindInfo info = { NULL, modem }; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMPluginBaseSupportsTaskPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; +} + +/*****************************************************************************/ + +#define TAG_PROBE_ID "mm-plugin-base-probe-id" +#define TAG_PROBE_PORT "mm-plugin-base-probe-port" +#define TAG_PROBE_STATE "mm-plugin-base-probe-state" + +#define MM_PLUGIN_BASE_PORT_CAP_CDMA (MM_PLUGIN_BASE_PORT_CAP_IS707_A | \ + MM_PLUGIN_BASE_PORT_CAP_IS707_P | \ + MM_PLUGIN_BASE_PORT_CAP_IS856 | \ + MM_PLUGIN_BASE_PORT_CAP_IS856_A) + +#define CAP_GSM_OR_CDMA (MM_PLUGIN_BASE_PORT_CAP_CDMA | MM_PLUGIN_BASE_PORT_CAP_GSM) + +struct modem_caps { + char *name; + guint32 bits; +}; + +static struct modem_caps modem_caps[] = { + {"+CGSM", MM_PLUGIN_BASE_PORT_CAP_GSM}, + {"+CIS707-A", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, + {"+CIS707A", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, /* Cmotech */ + {"+CIS707", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, + {"CIS707", MM_PLUGIN_BASE_PORT_CAP_IS707_A}, /* Qualcomm Gobi */ + {"+CIS707P", MM_PLUGIN_BASE_PORT_CAP_IS707_P}, + {"CIS-856", MM_PLUGIN_BASE_PORT_CAP_IS856}, + {"+IS-856", MM_PLUGIN_BASE_PORT_CAP_IS856}, /* Cmotech */ + {"CIS-856-A", MM_PLUGIN_BASE_PORT_CAP_IS856_A}, + {"CIS-856A", MM_PLUGIN_BASE_PORT_CAP_IS856_A}, /* Kyocera KPC680 */ + {"+DS", MM_PLUGIN_BASE_PORT_CAP_DS}, + {"+ES", MM_PLUGIN_BASE_PORT_CAP_ES}, + {"+MS", MM_PLUGIN_BASE_PORT_CAP_MS}, + {"+FCLASS", MM_PLUGIN_BASE_PORT_CAP_FCLASS}, + {NULL} +}; + +static guint32 +parse_gcap (const char *buf) +{ + struct modem_caps *cap = modem_caps; + guint32 ret = 0; + + while (cap->name) { + if (strstr (buf, cap->name)) + ret |= cap->bits; + cap++; + } + return ret; +} + +static guint32 +parse_cgmm (const char *buf) +{ + if (strstr (buf, "GSM900") || strstr (buf, "GSM1800") || + strstr (buf, "GSM1900") || strstr (buf, "GSM850")) + return MM_PLUGIN_BASE_PORT_CAP_GSM; + return 0; +} + +static gboolean +emit_probe_result (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMPlugin *self = mm_plugin_base_supports_task_get_plugin (task); + + /* Close the serial port */ + g_object_unref (task_priv->probe_port); + task_priv->probe_port = NULL; + + task_priv->probe_id = 0; + g_signal_emit (self, signals[PROBE_RESULT], 0, task, task_priv->probed_caps); + return FALSE; +} + +static void +probe_complete (MMPluginBaseSupportsTask *task) +{ + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + g_hash_table_insert (cached_caps, + g_strdup (mm_port_get_device (MM_PORT (task_priv->probe_port))), + GUINT_TO_POINTER (task_priv->probed_caps)); + + task_priv->probe_id = g_idle_add (emit_probe_result, task); +} + +static void +parse_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data); + +static void +real_handle_probe_response (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + const char *cmd, + const char *response, + const GError *error) +{ + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMSerialPort *port = task_priv->probe_port; + + task_priv->probe_state++; + + if (error) { + if (error->code == MM_SERIAL_RESPONSE_TIMEOUT) { + /* Try GCAP again */ + if (task_priv->probe_state <= PROBE_STATE_GCAP_TRY3) { + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); + return; + } + + /* Otherwise, if all the GCAP tries timed out, ignore the port + * as it's probably not an AT-capable port. + */ + probe_complete (task); + } else if (task_priv->probe_state <= PROBE_STATE_GCAP_TRY3) { + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); + } else if (task_priv->probe_state == PROBE_STATE_ATI) { + /* Give ATI a try */ + mm_serial_port_queue_command (port, "I", 3, parse_response, task); + } else if (task_priv->probe_state == PROBE_STATE_CGMM) { + /* If CGMM failed, probably not a modem port */ + probe_complete (task); + } + return; + } + + if (response) { + /* Parse the response */ + task_priv->probed_caps = parse_gcap (response); + + /* Some models (BUSlink SCWi275u) stick stupid stuff in the GMM response */ + if ( (task_priv->probe_state == PROBE_STATE_CGMM) + && !(task_priv->probed_caps & CAP_GSM_OR_CDMA)) + task_priv->probed_caps = parse_cgmm (response); + + if (task_priv->probed_caps & CAP_GSM_OR_CDMA) { + probe_complete (task); + return; + } + } + + /* Try a different command */ + switch (task_priv->probe_state) { + case PROBE_STATE_GCAP_TRY1: + case PROBE_STATE_GCAP_TRY2: + case PROBE_STATE_GCAP_TRY3: + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, task); + break; + case PROBE_STATE_ATI: + /* After the last GCAP attempt, try ATI */ + mm_serial_port_queue_command (port, "I", 3, parse_response, task); + break; + case PROBE_STATE_CGMM: + /* After the ATI attempt, try CGMM */ + mm_serial_port_queue_command (port, "+CGMM", 3, parse_response, task); + break; + default: + /* Probably not GSM or CDMA */ + probe_complete (task); + break; + } +} + +static gboolean +handle_probe_response (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMPluginBase *self = MM_PLUGIN_BASE (mm_plugin_base_supports_task_get_plugin (task)); + const char *cmd = NULL; + + switch (task_priv->probe_state) { + case PROBE_STATE_GCAP_TRY1: + case PROBE_STATE_GCAP_TRY2: + case PROBE_STATE_GCAP_TRY3: + cmd = "+GCAP"; + break; + case PROBE_STATE_ATI: + cmd = "I"; + break; + case PROBE_STATE_CGMM: + default: + cmd = "+CGMM"; + break; + } + + MM_PLUGIN_BASE_GET_CLASS (self)->handle_probe_response (self, + task, + cmd, + task_priv->probe_resp, + task_priv->probe_error); + return FALSE; +} + +static void +parse_response (MMSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + if (task_priv->probe_id) + g_source_remove (task_priv->probe_id); + g_free (task_priv->probe_resp); + task_priv->probe_resp = NULL; + g_clear_error (&(task_priv->probe_error)); + + if (response && response->len) + task_priv->probe_resp = g_strdup (response->str); + if (error) + task_priv->probe_error = g_error_copy (error); + + /* Schedule the response handler in an idle, since we can't emit the + * PROBE_RESULT signal from the serial port response handler without + * potentially destroying the serial port in the middle of its response + * handler, which it understandably doesn't like. + */ + task_priv->probe_id = g_idle_add (handle_probe_response, task); +} - g_hash_table_foreach (priv->modems, find_modem, &info); - if (info.key) - g_hash_table_remove (priv->modems, info.key); - g_free (info.key); +static void +flash_done (MMSerialPort *port, gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + + task_priv->probe_id = 0; + mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data); } gboolean -mm_plugin_base_add_modem (MMPluginBase *self, - MMModem *modem) +mm_plugin_base_probe_port (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + GError **error) { - MMPluginBasePrivate *priv; - const char *device; + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + MMSerialPort *serial; + const char *name; + GUdevDevice *port; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), FALSE); - g_return_val_if_fail (modem != NULL, FALSE); - g_return_val_if_fail (MM_IS_MODEM (modem), FALSE); - - priv = MM_PLUGIN_BASE_GET_PRIVATE (self); - - device = mm_modem_get_device (modem); - if (g_hash_table_lookup (priv->modems, device)) + g_return_val_if_fail (task != NULL, FALSE); + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + name = g_udev_device_get_name (port); + g_assert (name); + + serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY); + g_object_set (serial, + MM_SERIAL_PORT_SEND_DELAY, 100000, + MM_PORT_CARRIER_DETECT, FALSE, + NULL); + + mm_serial_port_set_response_parser (serial, + mm_serial_parser_v1_parse, + mm_serial_parser_v1_new (), + mm_serial_parser_v1_destroy); + + if (!mm_serial_port_open (serial, error)) { + g_object_unref (serial); return FALSE; + } - g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self); - g_hash_table_insert (priv->modems, g_strdup (device), modem); + g_debug ("(%s): probe requested by plugin '%s'", name, priv->name); + task_priv->probe_port = serial; + task_priv->probe_id = mm_serial_port_flash (serial, 100, flash_done, task); return TRUE; } +gboolean +mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self, + GUdevDevice *port, + guint32 *capabilities) +{ + return g_hash_table_lookup_extended (cached_caps, + g_udev_device_get_name (port), + NULL, + (gpointer) capabilities); +} + +/*****************************************************************************/ + +static void +modem_destroyed (gpointer data, GObject *modem) +{ + MMPluginBase *self = MM_PLUGIN_BASE (data); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, value; + + /* Remove it from the modems info */ + g_hash_table_iter_init (&iter, priv->modems); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (value == modem) { + g_hash_table_iter_remove (&iter); + break; + } + } + + /* Since we don't track port cached capabilities on a per-modem basis, + * we just have to live with blowing away the cached capabilities whenever + * a modem gets removed. Could do better here by storing a structure + * in the cached capabilities table that includes { caps, modem device } + * or something and then only removing cached capabilities for ports + * that the modem that was just removed owned, but whatever. + */ + g_hash_table_remove_all (cached_caps); +} + MMModem * mm_plugin_base_find_modem (MMPluginBase *self, const char *master_device) @@ -132,9 +595,8 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, guint16 *vendor, guint16 *product) { - GUdevClient *client; + MMPluginBasePrivate *priv; GUdevDevice *device = NULL; - const char *tmp[] = { subsys, NULL }; const char *vid, *pid; gboolean success = FALSE; @@ -147,11 +609,9 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, if (product) g_return_val_if_fail (*product == 0, FALSE); - client = g_udev_client_new (tmp); - if (!client) - return FALSE; + priv = MM_PLUGIN_BASE_GET_PRIVATE (self); - device = g_udev_client_query_by_subsystem_and_name (client, subsys, name); + device = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name); if (!device) goto out; @@ -180,18 +640,255 @@ mm_plugin_base_get_device_ids (MMPluginBase *self, out: if (device) g_object_unref (device); - g_object_unref (client); return success; } +static char * +get_key (const char *subsys, const char *name) +{ + return g_strdup_printf ("%s%s", subsys, name); +} + +static const char * +get_name (MMPlugin *plugin) +{ + return MM_PLUGIN_BASE_GET_PRIVATE (plugin)->name; +} + +static char * +get_driver_name (GUdevDevice *device) +{ + GUdevDevice *parent = NULL; + const char *driver; + char *ret; + + driver = g_udev_device_get_driver (device); + if (!driver) { + parent = g_udev_device_get_parent (device); + if (parent) + driver = g_udev_device_get_driver (parent); + } + + if (driver) + ret = g_strdup (driver); + if (parent) + g_object_unref (parent); + + return ret; +} + +static GUdevDevice * +real_find_physical_device (MMPluginBase *plugin, GUdevDevice *child) +{ + GUdevDevice *iter, *old = NULL; + const char *bus, *type; + + g_return_val_if_fail (child != NULL, NULL); + + bus = g_udev_device_get_property (child, "ID_BUS"); + if (!bus) + return NULL; + + if (!strcmp (bus, "usb")) { + /* Walk the parents to find the 'usb_device' for this device. */ + iter = g_object_ref (child); + while (iter) { + type = g_udev_device_get_devtype (iter); + if (type && !strcmp (type, "usb_device")) + return iter; + + old = iter; + iter = g_udev_device_get_parent (old); + g_object_unref (old); + } + g_object_unref (child); + } else if (!strcmp (bus, "pci")) { + return g_udev_device_get_parent (child); + } + + // FIXME: pci and pcmcia/cardbus? (like Sierra 850/860) + return NULL; +} + +static MMPluginSupportsResult +supports_port (MMPlugin *plugin, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer callback_data) +{ + MMPluginBase *self = MM_PLUGIN_BASE (plugin); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + GUdevDevice *port = NULL, *physdev = NULL; + char *driver = NULL, *key = NULL; + MMPluginBaseSupportsTask *task; + MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; + MMModem *existing; + const char *master_path; + + key = get_key (subsys, name); + task = g_hash_table_lookup (priv->tasks, key); + if (task) { + g_free (key); + g_return_val_if_fail (task == NULL, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED); + } + + port = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name); + if (!port) + goto out; + + physdev = MM_PLUGIN_BASE_GET_CLASS (self)->find_physical_device (self, port); + if (!physdev) + goto out; + + driver = get_driver_name (port); + if (!driver) + goto out; + + task = supports_task_new (self, port, physdev, driver, callback, callback_data); + g_assert (task); + g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (task)); + + /* Help the plugin out a bit by finding an existing modem for this port */ + master_path = g_udev_device_get_sysfs_path (physdev); + existing = g_hash_table_lookup (priv->modems, master_path); + + result = MM_PLUGIN_BASE_GET_CLASS (self)->supports_port (self, existing, task); + if (result != MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS) { + /* 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); + +out: + if (physdev) + g_object_unref (physdev); + if (port) + g_object_unref (port); + g_free (key); + g_free (driver); + return result; +} + +static void +cancel_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name) +{ + MMPluginBase *self = MM_PLUGIN_BASE (plugin); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + MMPluginBaseSupportsTask *task; + char *key; + + key = get_key (subsys, name); + task = g_hash_table_lookup (priv->tasks, key); + if (task) { + /* Let the plugin cancel any ongoing tasks */ + if (MM_PLUGIN_BASE_GET_CLASS (self)->cancel_task) + MM_PLUGIN_BASE_GET_CLASS (self)->cancel_task (self, task); + g_hash_table_remove (priv->tasks, key); + } + + g_free (key); +} + +static MMModem * +grab_port (MMPlugin *plugin, + const char *subsys, + const char *name, + GError **error) +{ + MMPluginBase *self = MM_PLUGIN_BASE (plugin); + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + MMPluginBaseSupportsTask *task; + char *key; + MMModem *existing = NULL, *modem = NULL; + const char *master_path; + + key = get_key (subsys, name); + task = g_hash_table_lookup (priv->tasks, key); + if (!task) { + g_free (key); + g_return_val_if_fail (task != NULL, FALSE); + } + + /* Help the plugin out a bit by finding an existing modem for this port */ + master_path = g_udev_device_get_sysfs_path (mm_plugin_base_supports_task_get_physdev (task)); + existing = g_hash_table_lookup (priv->modems, master_path); + + /* Let the modem grab the port */ + modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self, existing, task, error); + if (modem && !existing) { + g_hash_table_insert (priv->modems, g_strdup (master_path), modem); + g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self); + } + + g_hash_table_remove (priv->tasks, key); + g_free (key); + return modem; +} + /*****************************************************************************/ static void +plugin_init (MMPlugin *plugin_class) +{ + /* interface implementation */ + plugin_class->get_name = get_name; + plugin_class->supports_port = supports_port; + plugin_class->cancel_supports_port = cancel_supports_port; + plugin_class->grab_port = grab_port; +} + +static void mm_plugin_base_init (MMPluginBase *self) { MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + const char *subsys[] = { "tty", "net", NULL }; + + if (!cached_caps) + cached_caps = g_hash_table_new (g_str_hash, g_str_equal); + + priv->client = g_udev_client_new (subsys); priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) g_object_unref); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + /* Construct only */ + priv->name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } static void @@ -199,7 +896,12 @@ finalize (GObject *object) { MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (object); + g_free (priv->name); + + g_object_unref (priv->client); + g_hash_table_destroy (priv->modems); + g_hash_table_destroy (priv->tasks); G_OBJECT_CLASS (mm_plugin_base_parent_class)->finalize (object); } @@ -211,6 +913,28 @@ mm_plugin_base_class_init (MMPluginBaseClass *klass) g_type_class_add_private (object_class, sizeof (MMPluginBasePrivate)); + klass->find_physical_device = real_find_physical_device; + klass->handle_probe_response = real_handle_probe_response; + /* Virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (MM_PLUGIN_BASE_NAME, + "Name", + "Name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + signals[PROBE_RESULT] = + g_signal_new ("probe-result", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MMPluginBaseClass, probe_result), + NULL, NULL, + mm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); } diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h index 92e46d90..e426956d 100644 --- a/src/mm-plugin-base.h +++ b/src/mm-plugin-base.h @@ -20,7 +20,54 @@ #include <glib/gtypes.h> #include <glib-object.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> + +#include "mm-plugin.h" #include "mm-modem.h" +#include "mm-port.h" + +#define MM_PLUGIN_BASE_PORT_CAP_GSM 0x0001 /* GSM */ +#define MM_PLUGIN_BASE_PORT_CAP_IS707_A 0x0002 /* CDMA Circuit Switched Data */ +#define MM_PLUGIN_BASE_PORT_CAP_IS707_P 0x0004 /* CDMA Packet Switched Data */ +#define MM_PLUGIN_BASE_PORT_CAP_DS 0x0008 /* Data compression selection (v.42bis) */ +#define MM_PLUGIN_BASE_PORT_CAP_ES 0x0010 /* Error control selection (v.42) */ +#define MM_PLUGIN_BASE_PORT_CAP_FCLASS 0x0020 /* Group III Fax */ +#define MM_PLUGIN_BASE_PORT_CAP_MS 0x0040 /* Modulation selection */ +#define MM_PLUGIN_BASE_PORT_CAP_W 0x0080 /* Wireless commands */ +#define MM_PLUGIN_BASE_PORT_CAP_IS856 0x0100 /* CDMA 3G EVDO rev 0 */ +#define MM_PLUGIN_BASE_PORT_CAP_IS856_A 0x0200 /* CDMA 3G EVDO rev A */ + +#define MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK (mm_plugin_base_supports_task_get_type ()) +#define MM_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTask)) +#define MM_PLUGIN_BASE_SUPPORTS_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskClass)) +#define MM_IS_PLUGIN_BASE_SUPPORTS_TASK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK)) +#define MM_IS_PLUBIN_BASE_SUPPORTS_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK)) +#define MM_PLUGIN_BASE_SUPPORTS_TASK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_BASE_SUPPORTS_TASK, MMPluginBaseSupportsTaskClass)) + +typedef struct { + GObject parent; +} MMPluginBaseSupportsTask; + +typedef struct { + GObjectClass parent; +} MMPluginBaseSupportsTaskClass; + +GType mm_plugin_base_supports_task_get_type (void); + +MMPlugin *mm_plugin_base_supports_task_get_plugin (MMPluginBaseSupportsTask *task); + +GUdevDevice *mm_plugin_base_supports_task_get_port (MMPluginBaseSupportsTask *task); + +GUdevDevice *mm_plugin_base_supports_task_get_physdev (MMPluginBaseSupportsTask *task); + +const char *mm_plugin_base_supports_task_get_driver (MMPluginBaseSupportsTask *task); + +guint32 mm_plugin_base_supports_task_get_probed_capabilities (MMPluginBaseSupportsTask *task); + +void mm_plugin_base_supports_task_complete (MMPluginBaseSupportsTask *task, + guint32 level); + #define MM_TYPE_PLUGIN_BASE (mm_plugin_base_get_type ()) #define MM_PLUGIN_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BASE, MMPluginBase)) @@ -29,6 +76,8 @@ #define MM_IS_PLUBIN_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_BASE)) #define MM_PLUGIN_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_BASE, MMPluginBaseClass)) +#define MM_PLUGIN_BASE_NAME "name" + typedef struct _MMPluginBase MMPluginBase; typedef struct _MMPluginBaseClass MMPluginBaseClass; @@ -38,13 +87,38 @@ struct _MMPluginBase { struct _MMPluginBaseClass { GObjectClass parent; + + /* Mandatory subclass functions */ + MMPluginSupportsResult (*supports_port) (MMPluginBase *plugin, + MMModem *existing, + MMPluginBaseSupportsTask *task); + + MMModem *(*grab_port) (MMPluginBase *plugin, + MMModem *existing, + MMPluginBaseSupportsTask *task, + GError **error); + + /* Optional subclass functions */ + void (*cancel_task) (MMPluginBase *plugin, + MMPluginBaseSupportsTask *task); + + GUdevDevice * (*find_physical_device) (MMPluginBase *plugin, + GUdevDevice *port); + + void (*handle_probe_response) (MMPluginBase *plugin, + MMPluginBaseSupportsTask *task, + const char *command, + const char *response, + const GError *error); + + /* Signals */ + void (*probe_result) (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + guint32 capabilities); }; GType mm_plugin_base_get_type (void); -gboolean mm_plugin_base_add_modem (MMPluginBase *self, - MMModem *modem); - MMModem *mm_plugin_base_find_modem (MMPluginBase *self, const char *master_device); @@ -54,5 +128,14 @@ gboolean mm_plugin_base_get_device_ids (MMPluginBase *self, guint16 *vendor, guint16 *product); +gboolean mm_plugin_base_probe_port (MMPluginBase *self, + MMPluginBaseSupportsTask *task, + GError **error); + +/* Returns TRUE if the port was previously probed, FALSE if not */ +gboolean mm_plugin_base_get_cached_port_capabilities (MMPluginBase *self, + GUdevDevice *port, + guint32 *capabilities); + #endif /* MM_PLUGIN_BASE_H */ diff --git a/src/mm-plugin.c b/src/mm-plugin.c index c3425538..12df9dab 100644 --- a/src/mm-plugin.c +++ b/src/mm-plugin.c @@ -10,16 +10,31 @@ mm_plugin_get_name (MMPlugin *plugin) return MM_PLUGIN_GET_INTERFACE (plugin)->get_name (plugin); } -guint32 +MMPluginSupportsResult mm_plugin_supports_port (MMPlugin *plugin, const char *subsys, - const char *name) + const char *name, + MMSupportsPortResultFunc callback, + gpointer user_data) { g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); g_return_val_if_fail (subsys != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); - return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name); + return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name, callback, user_data); +} + +void +mm_plugin_cancel_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name) +{ + g_return_if_fail (MM_IS_PLUGIN (plugin)); + g_return_if_fail (subsys != NULL); + g_return_if_fail (name != NULL); + + MM_PLUGIN_GET_INTERFACE (plugin)->cancel_supports_port (plugin, subsys, name); } MMModem * diff --git a/src/mm-plugin.h b/src/mm-plugin.h index 696cf668..3fa3a2cb 100644 --- a/src/mm-plugin.h +++ b/src/mm-plugin.h @@ -6,6 +6,8 @@ #include <glib-object.h> #include <mm-modem.h> +#define MM_PLUGIN_GENERIC_NAME "Generic" + #define MM_PLUGIN_MAJOR_VERSION 3 #define MM_PLUGIN_MINOR_VERSION 0 @@ -18,6 +20,23 @@ typedef struct _MMPlugin MMPlugin; typedef MMPlugin *(*MMPluginCreateFunc) (void); +/* + * 'level' is a value between 0 and 100 inclusive, where 0 means the plugin has + * no support for the port, and 100 means the plugin has full support for the + * port. + */ +typedef void (*MMSupportsPortResultFunc) (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data); + +typedef enum { + MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED = 0x0, + MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS, + MM_PLUGIN_SUPPORTS_PORT_DEFER +} MMPluginSupportsResult; + struct _MMPlugin { GTypeInterface g_iface; @@ -25,14 +44,30 @@ struct _MMPlugin { const char *(*get_name) (MMPlugin *self); /* Check whether a plugin supports a particular modem port, and what level - * of support the plugin has for the device. The plugin should return a - * value between 0 and 100 inclusive, where 0 means the plugin has no - * support for the device, and 100 means the plugin has full support for the - * device. + * of support the plugin has for the device. If the plugin can immediately + * determine whether a port is unsupported, it should return + * FALSE. Otherwise, if the plugin needs to perform additional operations + * (ie, probing) to determine the level of support or additional details + * 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. */ - guint32 (*supports_port) (MMPlugin *self, - const char *subsys, - const char *name); + MMPluginSupportsResult (*supports_port) (MMPlugin *self, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer user_data); + + /* Called to cancel an ongoing supports_port() operation or to notify the + * plugin to clean up that operation. For example, if two plugins support + * a particular port, but the first plugin grabs the port, this method will + * be called on the second plugin to allow that plugin to clean up resources + * used while determining it's level of support for the port. + */ + void (*cancel_supports_port) (MMPlugin *self, + const char *subsys, + const char *name); /* Will only be called if the plugin returns a value greater than 0 for * the supports_port() method for this port. The plugin should create and @@ -52,9 +87,15 @@ GType mm_plugin_get_type (void); const char *mm_plugin_get_name (MMPlugin *plugin); -guint32 mm_plugin_supports_port (MMPlugin *plugin, - const char *subsys, - const char *name); +MMPluginSupportsResult mm_plugin_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name, + MMSupportsPortResultFunc callback, + gpointer user_data); + +void mm_plugin_cancel_supports_port (MMPlugin *plugin, + const char *subsys, + const char *name); MMModem *mm_plugin_grab_port (MMPlugin *plugin, const char *subsys, diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index 8e6b75b8..ae0c20da 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -87,6 +87,9 @@ mm_serial_parser_v0_parse (gpointer data, g_return_val_if_fail (parser != NULL, FALSE); g_return_val_if_fail (response != NULL, FALSE); + if (G_UNLIKELY (!response->len || !strlen (response->str))) + return FALSE; + found = g_regex_match_full (parser->generic_response, response->str, response->len, 0, 0, &match_info, NULL); if (found) { str = g_match_info_fetch (match_info, 1); @@ -205,7 +208,10 @@ mm_serial_parser_v1_parse (gpointer data, g_return_val_if_fail (parser != NULL, FALSE); g_return_val_if_fail (response != NULL, FALSE); - /* First, check for successfule responses */ + if (G_UNLIKELY (!response->len || !strlen (response->str))) + return FALSE; + + /* First, check for successful responses */ found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); if (found) diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index 564a8ace..99258f91 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -696,6 +696,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error) } priv->channel = g_io_channel_unix_new (priv->fd); + g_io_channel_set_encoding (priv->channel, NULL, NULL); priv->watch_id = g_io_add_watch (priv->channel, G_IO_IN | G_IO_ERR | G_IO_HUP, data_available, self); |