diff options
Diffstat (limited to 'src/mm-plugin-base.c')
-rw-r--r-- | src/mm-plugin-base.c | 796 |
1 files changed, 760 insertions, 36 deletions
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); } |