/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #define _GNU_SOURCE /* for strcasestr */ #include #include #include #include #include #include #include #define G_UDEV_API_IS_SUBJECT_TO_CHANGE #include #include "mm-plugin-base.h" #include "mm-port-probe-cache.h" #include "mm-at-serial-port.h" #include "mm-qcdm-serial-port.h" #include "mm-serial-parsers.h" #include "mm-marshal.h" #include "mm-utils.h" #include "mm-private-boxed-types.h" #include "libqcdm/src/commands.h" #include "libqcdm/src/utils.h" #include "libqcdm/src/errors.h" #include "mm-log.h" 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)) /* Virtual port corresponding to the embeded modem */ static gchar *virtual_port[] = {"smd0", NULL}; typedef struct { gchar *name; GUdevClient *client; GHashTable *tasks; /* Plugin-specific setups */ gchar **subsystems; gchar **drivers; guint16 *vendor_ids; mm_uint16_pair *product_ids; gchar **vendor_strings; mm_str_pair *product_strings; gchar **udev_tags; gboolean at; gboolean qcdm; MMPortProbeAtCommand *custom_init; guint64 send_delay; } MMPluginBasePrivate; enum { PROP_0, PROP_NAME, PROP_ALLOWED_SUBSYSTEMS, PROP_ALLOWED_DRIVERS, PROP_ALLOWED_VENDOR_IDS, PROP_ALLOWED_PRODUCT_IDS, PROP_ALLOWED_VENDOR_STRINGS, PROP_ALLOWED_PRODUCT_STRINGS, PROP_ALLOWED_UDEV_TAGS, PROP_ALLOWED_AT, PROP_ALLOWED_QCDM, PROP_CUSTOM_INIT, PROP_SEND_DELAY, LAST_PROP }; /*****************************************************************************/ gboolean mm_plugin_base_get_device_ids (MMPluginBase *self, const char *subsys, const char *name, guint16 *vendor, guint16 *product) { MMPluginBasePrivate *priv; GUdevDevice *device = NULL, *parent = NULL; const char *vid = NULL, *pid = NULL, *parent_subsys; gboolean success = FALSE; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (MM_IS_PLUGIN_BASE (self), FALSE); g_return_val_if_fail (subsys != NULL, FALSE); g_return_val_if_fail (name != NULL, FALSE); if (vendor) g_return_val_if_fail (*vendor == 0, FALSE); if (product) g_return_val_if_fail (*product == 0, FALSE); priv = MM_PLUGIN_BASE_GET_PRIVATE (self); device = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name); if (!device) goto out; parent = g_udev_device_get_parent (device); if (parent) { parent_subsys = g_udev_device_get_subsystem (parent); if (parent_subsys) { if (!strcmp (parent_subsys, "bluetooth")) { /* Bluetooth devices report the VID/PID of the BT adapter here, * which isn't really what we want. Just return null IDs instead. */ success = TRUE; goto out; } else if (!strcmp (parent_subsys, "pcmcia")) { /* For PCMCIA devices we need to grab the PCMCIA subsystem's * manfid and cardid, since any IDs on the tty device itself * may be from PCMCIA controller or something else. */ vid = g_udev_device_get_sysfs_attr (parent, "manf_id"); pid = g_udev_device_get_sysfs_attr (parent, "card_id"); if (!vid || !pid) goto out; } else if (!strcmp (parent_subsys, "platform")) { /* Platform devices don't usually have a VID/PID */ success = TRUE; goto out; } } } if (!vid) vid = g_udev_device_get_property (device, "ID_VENDOR_ID"); if (!vid) goto out; if (strncmp (vid, "0x", 2) == 0) vid += 2; if (strlen (vid) != 4) goto out; if (vendor) { *vendor = (guint16) (utils_hex2byte (vid + 2) & 0xFF); *vendor |= (guint16) ((utils_hex2byte (vid) & 0xFF) << 8); } if (!pid) pid = g_udev_device_get_property (device, "ID_MODEL_ID"); if (!pid) { *vendor = 0; goto out; } if (strncmp (pid, "0x", 2) == 0) pid += 2; if (strlen (pid) != 4) { *vendor = 0; goto out; } if (product) { *product = (guint16) (utils_hex2byte (pid + 2) & 0xFF); *product |= (guint16) ((utils_hex2byte (pid) & 0xFF) << 8); } success = TRUE; out: if (device) g_object_unref (device); if (parent) g_object_unref (parent); 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 gboolean get_sort_last (const MMPlugin *plugin) { MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (plugin); /* If we have any post-probing filter, we need to sort the plugin last */ return (priv->vendor_strings || priv->product_strings); } static char * get_driver_name (GUdevDevice *device) { GUdevDevice *parent = NULL; const char *driver, *subsys; char *ret = NULL; 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); /* Check for bluetooth; it's driver is a bunch of levels up so we * just check for the subsystem of the parent being bluetooth. */ if (!driver && parent) { subsys = g_udev_device_get_subsystem (parent); if (subsys && !strcmp (subsys, "bluetooth")) driver = "bluetooth"; } } if (driver) ret = g_strdup (driver); if (parent) g_object_unref (parent); return ret; } static gboolean device_file_exists (const char *name) { char *devfile; struct stat s; int result; devfile = g_strdup_printf ("/dev/%s", name); result = stat (devfile, &s); g_free (devfile); return (0 == result) ? TRUE : FALSE; } static gboolean is_virtual_port (const gchar *device_name) { guint idx; /* Detect any modems accessible through the list of virtual ports */ for (idx = 0; virtual_port[idx]; idx++) { if (strcmp (device_name, virtual_port[idx])) continue; if (!device_file_exists (virtual_port[idx])) continue; return TRUE; } return FALSE; } /* Returns TRUE if the support check request was filtered out */ static gboolean apply_pre_probing_filters (MMPluginBase *self, GUdevDevice *port, const gchar *subsys, const gchar *name, const gchar *driver, gboolean *need_vendor_probing, gboolean *need_product_probing) { MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); guint16 vendor = 0; guint16 product = 0; gboolean product_filtered = FALSE; gboolean vendor_filtered = FALSE; guint i; *need_vendor_probing = FALSE; *need_product_probing = FALSE; /* The plugin may specify that only some subsystems are supported. If that * is the case, filter by subsystem */ if (priv->subsystems) { for (i = 0; priv->subsystems[i]; i++) { if (g_str_equal (subsys, priv->subsystems[i])) break; } /* If we didn't match any subsystem: unsupported */ if (!priv->subsystems[i]) return TRUE; } /* The plugin may specify that only some drivers are supported. If that * is the case, filter by driver */ if (priv->drivers) { for (i = 0; priv->drivers[i]; i++) { if (g_str_equal (driver, priv->drivers[i])) break; } /* If we didn't match any driver: unsupported */ if (!priv->drivers[i]) return TRUE; } mm_plugin_base_get_device_ids (self, subsys, name, &vendor, &product); /* The plugin may specify that only some vendor IDs are supported. If that * is the case, filter by vendor ID. */ if (priv->vendor_ids) { /* If we didn't get any vendor: filtered */ if (!vendor) vendor_filtered = TRUE; else { for (i = 0; priv->vendor_ids[i]; i++) if (vendor == priv->vendor_ids[i]) break; /* If we didn't match any vendor: filtered */ if (!priv->vendor_ids[i]) vendor_filtered = TRUE; } } /* The plugin may specify that only some product IDs are supported. If * that is the case, filter by vendor+product ID pair */ if (priv->product_ids) { /* If we didn't get any product: filtered */ if (!product) product_filtered = TRUE; else { for (i = 0; priv->product_ids[i].l; i++) if (vendor == priv->product_ids[i].l && product == priv->product_ids[i].r) break; /* If we didn't match any product: filtered */ if (!priv->product_ids[i].l) product_filtered = TRUE; } } /* If we got filtered by vendor or product IDs and we do not have vendor * or product strings to compare with: unsupported */ if ((vendor_filtered || product_filtered) && !priv->vendor_strings && !priv->product_strings) return TRUE; /* If we need to filter by vendor/product strings, need to probe for both. * This covers the case where a RS232 modem is connected via a USB<->RS232 * adaptor, and we get in udev the vendor ID of the adaptor */ if (priv->product_strings) { *need_vendor_probing = TRUE; *need_product_probing = TRUE; } else if (priv->vendor_strings) *need_vendor_probing = TRUE; /* The plugin may specify that only ports with some given udev tags are * supported. If that is the case, filter by udev tag */ if (priv->udev_tags) { for (i = 0; priv->udev_tags[i]; i++) { /* Check if the port was tagged */ if (g_udev_device_get_property_as_boolean (port, priv->udev_tags[i])) break; } /* If we didn't match any udev tag: unsupported */ if (!priv->udev_tags[i]) return TRUE; } return FALSE; } /* Returns TRUE if the support check request was filtered out */ static gboolean apply_post_probing_filters (MMPluginBase *self, MMPortProbe *probe) { MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); gboolean vendor_filtered = FALSE; gboolean product_filtered = FALSE; guint i; /* The plugin may specify that only some vendor strings are supported. If * that is the case, filter by vendor string. */ if (priv->vendor_strings) { const gchar *vendor; vendor = mm_port_probe_get_vendor (probe); /* If we didn't get any vendor: filtered */ if (!vendor) vendor_filtered = TRUE; else { for (i = 0; priv->vendor_strings[i]; i++) { gboolean found; gchar *casefolded; casefolded = g_utf8_casefold (priv->vendor_strings[i], -1); found = !!strstr (vendor, casefolded); g_free (casefolded); if (found) break; } /* If we didn't match any vendor: filtered */ if (!priv->vendor_strings[i]) vendor_filtered = TRUE; } if (vendor_filtered) { if (!priv->product_strings) return TRUE; } else /* Vendor matched */ return FALSE; } /* The plugin may specify that only some vendor+product string pairs are * supported. If that is the case, filter by product string */ if (priv->product_strings) { const gchar *vendor; const gchar *product; vendor = mm_port_probe_get_vendor (probe); product = mm_port_probe_get_product (probe); /* If we didn't get any vendor or product: filtered */ if (!vendor || !product) product_filtered = TRUE; else { for (i = 0; priv->product_strings[i].l; i++) { gboolean found; gchar *casefolded_vendor; gchar *casefolded_product; casefolded_vendor = g_utf8_casefold (priv->product_strings[i].l, -1); casefolded_product = g_utf8_casefold (priv->product_strings[i].r, -1); found = (!!strstr (vendor, casefolded_vendor) && !!strstr (product, casefolded_product)); g_free (casefolded_vendor); g_free (casefolded_product); if (found) break; } /* If we didn't match any product: unsupported */ if (!priv->product_strings[i].l) product_filtered = TRUE; } return product_filtered; } return FALSE; } /* Context for the asynchronous probing operation */ typedef struct { GSimpleAsyncResult *result; MMPluginBase *plugin; } PortProbeRunContext; static void port_probe_run_ready (MMPortProbe *probe, GAsyncResult *probe_result, PortProbeRunContext *ctx) { GError *error = NULL; gboolean keep_probe = FALSE; if (!mm_port_probe_run_finish (probe, probe_result, &error)) { /* Probing failed */ g_simple_async_result_take_error (ctx->result, error); } else { /* Probing succeeded */ MMPluginSupportsResult supports_result; if (!apply_post_probing_filters (ctx->plugin, probe)) { /* Port is supported! Leave it in the internal HT until port gets * grabbed. */ supports_result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED; keep_probe = TRUE; } else { /* Unsupported port, remove from internal tracking HT */ supports_result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (supports_result), NULL); } /* Complete the async supports port request */ g_simple_async_result_complete_in_idle (ctx->result); /* If no longer needed, Remove probe from internal HT */ if (!keep_probe) { MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (ctx->plugin); gchar *key; key = get_key (mm_port_probe_get_port_subsys (probe), mm_port_probe_get_port_name (probe)); g_hash_table_remove (priv->tasks, key); g_free (key); } g_object_unref (ctx->result); g_object_unref (ctx->plugin); g_free (ctx); } static MMPluginSupportsResult supports_port_finish (MMPlugin *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (MM_IS_PLUGIN (self), MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED); /* Propagate error, if any */ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED; } return (MMPluginSupportsResult) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result))); } static void supports_port (MMPlugin *plugin, const gchar *subsys, const gchar *name, const gchar *physdev_path, MMBaseModem *existing, GAsyncReadyCallback callback, gpointer user_data) { MMPluginBase *self = MM_PLUGIN_BASE (plugin); MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); GUdevDevice *port = NULL; gchar *driver = NULL; gchar *key = NULL; MMPortProbe *probe; GSimpleAsyncResult *async_result; PortProbeRunContext *ctx; gboolean need_vendor_probing; gboolean need_product_probing; guint32 probe_run_flags; /* Setup key */ key = get_key (subsys, name); async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, supports_port); /* Get port device */ if (!(port = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name))) { g_simple_async_result_set_error (async_result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find port for (%s/%s)", subsys, name); g_simple_async_result_complete_in_idle (async_result); goto out; } /* Detect any modems accessible through the list of virtual ports */ if (!(driver = (is_virtual_port (name) ? g_strdup ("virtual") : get_driver_name (port)))) { g_simple_async_result_set_error (async_result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't find driver for (%s/%s)", subsys, name); g_simple_async_result_complete_in_idle (async_result); goto out; } /* Apply filters before launching the probing */ if (apply_pre_probing_filters (self, port, subsys, name, driver, &need_vendor_probing, &need_product_probing)) { /* Filtered! */ g_simple_async_result_set_op_res_gpointer (async_result, GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED), NULL); g_simple_async_result_complete_in_idle (async_result); goto out; } mm_dbg ("(%s) checking port support (%s,%s)", priv->name, subsys, name); /* Need to launch new probing */ /* Lookup current probes, there shouldn't be any (unless for net devices) */ probe = g_hash_table_lookup (priv->tasks, key); if (!probe) probe = mm_port_probe_cache_get (port, physdev_path, driver); g_assert (probe); /* Before launching any probing, check if the port is a net device (which * cannot be probed). * TODO: With the new defer-until-suggested we probably don't need the modem * object being passed down here just for this. */ if (g_str_equal (subsys, "net")) { /* Keep track of the probe object, which is considered finished */ if (!g_hash_table_lookup (priv->tasks, key)) g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (probe)); /* If we already have a existing modem, then mark it as supported. * Otherwise, just defer a bit */ g_simple_async_result_set_op_res_gpointer ( async_result, GUINT_TO_POINTER ((existing ? MM_PLUGIN_SUPPORTS_PORT_SUPPORTED : MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED)), NULL); g_simple_async_result_complete_in_idle (async_result); goto out; } /* Build flags depending on what probing needed */ probe_run_flags = 0; if (priv->at) probe_run_flags |= MM_PORT_PROBE_AT; if (need_vendor_probing) probe_run_flags |= MM_PORT_PROBE_AT_VENDOR; if (need_product_probing) probe_run_flags |= MM_PORT_PROBE_AT_PRODUCT; if (priv->qcdm) probe_run_flags |= MM_PORT_PROBE_QCDM; g_assert (probe_run_flags != 0); /* Setup async call context */ ctx = g_new (PortProbeRunContext, 1); ctx->plugin = g_object_ref (self); ctx->result = g_object_ref (async_result); /* Launch the probe */ mm_dbg ("(%s) launching probe for (%s,%s)", priv->name, subsys, name); mm_port_probe_run (probe, probe_run_flags, priv->send_delay, priv->custom_init, (GAsyncReadyCallback)port_probe_run_ready, ctx); /* Keep track of the probe */ g_hash_table_insert (priv->tasks, g_strdup (key), g_object_ref (probe)); out: if (port) g_object_unref (port); g_free (key); g_free (driver); g_object_unref (async_result); } static void supports_port_cancel (MMPlugin *plugin, const char *subsys, const char *name) { MMPluginBase *self = MM_PLUGIN_BASE (plugin); MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); MMPortProbe *probe; gchar *key; key = get_key (subsys, name); probe = g_hash_table_lookup (priv->tasks, key); if (probe) { mm_port_probe_run_cancel (probe); g_hash_table_remove (priv->tasks, key); } g_free (key); } static MMBaseModem * grab_port (MMPlugin *plugin, const char *subsys, const char *name, MMBaseModem *existing, GError **error) { MMPluginBase *self = MM_PLUGIN_BASE (plugin); MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); MMPortProbe *probe; MMBaseModem *modem = NULL; char *key; key = get_key (subsys, name); probe = g_hash_table_lookup (priv->tasks, key); g_assert (probe); /* Let the modem grab the port */ modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self, existing, probe, error); 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->get_sort_last = get_sort_last; plugin_class->supports_port = supports_port; plugin_class->supports_port_finish = supports_port_finish; plugin_class->supports_port_cancel = supports_port_cancel; 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 }; priv->client = g_udev_client_new (subsys); priv->tasks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); /* Defaults */ priv->send_delay = 100000; } 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; case PROP_ALLOWED_SUBSYSTEMS: /* Construct only */ priv->subsystems = g_value_dup_boxed (value); break; case PROP_ALLOWED_DRIVERS: /* Construct only */ priv->drivers = g_value_dup_boxed (value); break; case PROP_ALLOWED_VENDOR_IDS: /* Construct only */ priv->vendor_ids = g_value_dup_boxed (value); break; case PROP_ALLOWED_PRODUCT_IDS: /* Construct only */ priv->product_ids = g_value_dup_boxed (value); break; case PROP_ALLOWED_VENDOR_STRINGS: /* Construct only */ priv->vendor_strings = g_value_dup_boxed (value); break; case PROP_ALLOWED_PRODUCT_STRINGS: /* Construct only */ priv->product_strings = g_value_dup_boxed (value); break; case PROP_ALLOWED_UDEV_TAGS: /* Construct only */ priv->udev_tags = g_value_dup_boxed (value); break; case PROP_ALLOWED_AT: /* Construct only */ priv->at = g_value_get_boolean (value); break; case PROP_ALLOWED_QCDM: /* Construct only */ priv->qcdm = g_value_get_boolean (value); break; case PROP_CUSTOM_INIT: /* Construct only */ priv->custom_init = g_value_dup_boxed (value); break; case PROP_SEND_DELAY: /* Construct only */ priv->send_delay = (guint64)g_value_get_uint64 (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; case PROP_ALLOWED_SUBSYSTEMS: g_value_set_boxed (value, priv->subsystems); break; case PROP_ALLOWED_DRIVERS: g_value_set_boxed (value, priv->drivers); break; case PROP_ALLOWED_VENDOR_IDS: g_value_set_boxed (value, priv->vendor_ids); break; case PROP_ALLOWED_PRODUCT_IDS: g_value_set_boxed (value, priv->product_ids); break; case PROP_ALLOWED_VENDOR_STRINGS: g_value_set_boxed (value, priv->vendor_strings); break; case PROP_ALLOWED_PRODUCT_STRINGS: g_value_set_boxed (value, priv->product_strings); break; case PROP_ALLOWED_AT: g_value_set_boolean (value, priv->at); break; case PROP_ALLOWED_QCDM: g_value_set_boolean (value, priv->qcdm); break; case PROP_ALLOWED_UDEV_TAGS: g_value_set_boxed (value, priv->udev_tags); break; case PROP_CUSTOM_INIT: g_value_set_boxed (value, priv->custom_init); break; case PROP_SEND_DELAY: g_value_set_uint64 (value, priv->send_delay); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void 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->tasks); G_OBJECT_CLASS (mm_plugin_base_parent_class)->finalize (object); } static void mm_plugin_base_class_init (MMPluginBaseClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMPluginBasePrivate)); /* 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)); g_object_class_install_property (object_class, PROP_ALLOWED_SUBSYSTEMS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_SUBSYSTEMS, "Allowed subsystems", "List of subsystems this plugin can support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_DRIVERS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_DRIVERS, "Allowed drivers", "List of drivers this plugin can support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_VENDOR_IDS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_VENDOR_IDS, "Allowed vendor IDs", "List of vendor IDs this plugin can support, " "should be an array of guint16 finished with '0'", MM_TYPE_UINT16_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_PRODUCT_IDS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_PRODUCT_IDS, "Allowed product IDs", "List of vendor+product ID pairs this plugin can support, " "should be an array of mm_uint16_pair finished with '0,0'", MM_TYPE_UINT16_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_VENDOR_STRINGS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_VENDOR_STRINGS, "Allowed vendor strings", "List of vendor strings this plugin can support, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_PRODUCT_STRINGS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_PRODUCT_STRINGS, "Allowed product strings", "List of vendor+product string pairs this plugin can support, " "should be an array of mm_str_pair finished with 'NULL,NULL'", MM_TYPE_STR_PAIR_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_UDEV_TAGS, g_param_spec_boxed (MM_PLUGIN_BASE_ALLOWED_UDEV_TAGS, "Allowed Udev tags", "List of udev tags this plugin may expect, " "should be an array of strings finished with 'NULL'", G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_AT, g_param_spec_boolean (MM_PLUGIN_BASE_ALLOWED_AT, "Allowed AT", "Whether AT ports are allowed in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ALLOWED_QCDM, g_param_spec_boolean (MM_PLUGIN_BASE_ALLOWED_QCDM, "Allowed QCDM", "Whether QCDM ports are allowed in this plugin", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CUSTOM_INIT, g_param_spec_boxed (MM_PLUGIN_BASE_CUSTOM_INIT, "Custom initialization", "List of custom initializations this plugin needs, " "should be an array of MMPortProbeAtCommand structs " "finished with 'NULL'", MM_TYPE_POINTER_ARRAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SEND_DELAY, g_param_spec_uint64 (MM_PLUGIN_BASE_SEND_DELAY, "Send delay", "Send delay for characters in the AT port, " "in microseconds", 0, G_MAXUINT64, 100000, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); }