aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-generic-cdma.c1
-rw-r--r--src/mm-generic-gsm.c1
-rw-r--r--src/mm-manager.c313
-rw-r--r--src/mm-modem-base.c2
-rw-r--r--src/mm-modem.c3
-rw-r--r--src/mm-modem.h2
-rw-r--r--src/mm-plugin-base.c796
-rw-r--r--src/mm-plugin-base.h89
-rw-r--r--src/mm-plugin.c21
-rw-r--r--src/mm-plugin.h61
-rw-r--r--src/mm-serial-parsers.c8
-rw-r--r--src/mm-serial-port.c1
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);