Files
ModemManager/src/mm-plugin.c
T
Aleksander Morgado 30c2790aa4 kerneldevice,generic: load virtual devices without default rules
This is so that "make check" doesn't depend on having the rule files
installed in the default udev rules directory, which currently would
break the build:

    TEST: test-service-generic... (pid=2601)
      /MM/Service/Generic/enable-disable:                                  Activating service name='org.freedesktop.ModemManager1'
    Successfully activated service 'org.freedesktop.ModemManager1'

    ** (/home/aleksander/Development/foss/ModemManager/plugins/.libs/lt-test-service-generic:2601):
    ERROR **: Error setting test profile: GDBus.Error:org.freedesktop.ModemManager1.Error.Core.Failed: Failed to find primary AT port
    FAIL
    GTester: last random seed: R02S3897abaae9df36f8d2eeb679406ab675
    make[3]: *** [Makefile:3804: test-nonrecursive] Terminated
2016-11-21 13:44:08 +01:00

1456 lines
55 KiB
C

/* -*- 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 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Google, Inc.
*/
#define _GNU_SOURCE /* for strcasestr */
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <ModemManager.h>
#include <mm-errors-types.h>
#include "mm-plugin.h"
#include "mm-device.h"
#include "mm-kernel-device.h"
#include "mm-kernel-device-generic.h"
#include "mm-port-serial-at.h"
#include "mm-port-serial-qcdm.h"
#include "mm-serial-parsers.h"
#include "mm-private-boxed-types.h"
#include "mm-log.h"
#include "mm-daemon-enums-types.h"
G_DEFINE_TYPE (MMPlugin, mm_plugin, G_TYPE_OBJECT)
/* Virtual port corresponding to the embeded modem */
static gchar *virtual_port[] = {"smd0", NULL};
#define HAS_POST_PROBING_FILTERS(self) \
(self->priv->vendor_strings || \
self->priv->product_strings || \
self->priv->forbidden_product_strings || \
self->priv->allowed_icera || \
self->priv->forbidden_icera || \
self->priv->custom_init)
struct _MMPluginPrivate {
gchar *name;
GHashTable *tasks;
/* Pre-probing filters */
gchar **subsystems;
gchar **drivers;
gchar **forbidden_drivers;
guint16 *vendor_ids;
mm_uint16_pair *product_ids;
mm_uint16_pair *forbidden_product_ids;
gchar **udev_tags;
/* Post probing filters */
gchar **vendor_strings;
mm_str_pair *product_strings;
mm_str_pair *forbidden_product_strings;
gboolean allowed_icera;
gboolean forbidden_icera;
/* Probing setup */
gboolean at;
gboolean single_at;
gboolean qcdm;
gboolean qmi;
gboolean mbim;
gboolean icera_probe;
MMPortProbeAtCommand *custom_at_probe;
guint64 send_delay;
gboolean remove_echo;
gboolean send_lf;
/* Probing setup and/or post-probing filter.
* Plugins may use this method to decide whether they support a given
* port or not, so should also be considered kind of post-probing filter. */
MMAsyncMethod *custom_init;
};
enum {
PROP_0,
PROP_NAME,
PROP_ALLOWED_SUBSYSTEMS,
PROP_ALLOWED_DRIVERS,
PROP_FORBIDDEN_DRIVERS,
PROP_ALLOWED_VENDOR_IDS,
PROP_ALLOWED_PRODUCT_IDS,
PROP_FORBIDDEN_PRODUCT_IDS,
PROP_ALLOWED_VENDOR_STRINGS,
PROP_ALLOWED_PRODUCT_STRINGS,
PROP_FORBIDDEN_PRODUCT_STRINGS,
PROP_ALLOWED_UDEV_TAGS,
PROP_ALLOWED_AT,
PROP_ALLOWED_SINGLE_AT,
PROP_ALLOWED_QCDM,
PROP_ALLOWED_QMI,
PROP_ALLOWED_MBIM,
PROP_ICERA_PROBE,
PROP_ALLOWED_ICERA,
PROP_FORBIDDEN_ICERA,
PROP_CUSTOM_AT_PROBE,
PROP_CUSTOM_INIT,
PROP_SEND_DELAY,
PROP_REMOVE_ECHO,
PROP_SEND_LF,
LAST_PROP
};
/*****************************************************************************/
const gchar *
mm_plugin_get_name (MMPlugin *self)
{
return self->priv->name;
}
/*****************************************************************************/
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_subsystem_filter (MMPlugin *self,
MMKernelDevice *port)
{
if (self->priv->subsystems) {
const gchar *subsys;
guint i;
subsys = mm_kernel_device_get_subsystem (port);
for (i = 0; self->priv->subsystems[i]; i++) {
if (g_str_equal (subsys, self->priv->subsystems[i]))
break;
/* New kernels may report as 'usbmisc' the subsystem */
else if (g_str_equal (self->priv->subsystems[i], "usb") &&
g_str_equal (subsys, "usbmisc"))
break;
}
/* If we didn't match any subsystem: unsupported */
if (!self->priv->subsystems[i])
return TRUE;
}
return FALSE;
}
/* Returns TRUE if the support check request was filtered out */
static gboolean
apply_pre_probing_filters (MMPlugin *self,
MMDevice *device,
MMKernelDevice *port,
gboolean *need_vendor_probing,
gboolean *need_product_probing)
{
guint16 vendor;
guint16 product;
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 (apply_subsystem_filter (self, port)) {
mm_dbg ("(%s) [%s] filtered by subsystem",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
/* The plugin may specify that only some drivers are supported, or that some
* drivers are not supported. If that is the case, filter by driver.
*
* The QMI and MBIM *forbidden* drivers filter is implicit. This is, if the
* plugin doesn't explicitly specify that QMI is allowed and we find a QMI
* port, the plugin will filter the device. Same for MBIM.
*
* The opposite, though, is not applicable. If the plugin specifies that QMI
* is allowed, we won't take that as a mandatory requirement to look for the
* QMI driver (as the plugin may handle non-QMI modems as well)
*/
if (self->priv->drivers ||
self->priv->forbidden_drivers ||
!self->priv->qmi ||
!self->priv->mbim) {
static const gchar *virtual_drivers [] = { "virtual", NULL };
const gchar **drivers;
/* Detect any modems accessible through the list of virtual ports */
drivers = (is_virtual_port (mm_kernel_device_get_name (port)) ?
virtual_drivers :
mm_device_get_drivers (device));
/* If error retrieving driver: unsupported */
if (!drivers) {
mm_dbg ("(%s) [%s] filtered as couldn't retrieve drivers",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
/* Filtering by allowed drivers */
if (self->priv->drivers) {
gboolean found = FALSE;
for (i = 0; self->priv->drivers[i] && !found; i++) {
guint j;
for (j = 0; drivers[j] && !found; j++) {
if (g_str_equal (drivers[j], self->priv->drivers[i]))
found = TRUE;
}
}
/* If we didn't match any driver: unsupported */
if (!found) {
mm_dbg ("(%s) [%s] filtered by drivers",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
}
/* Filtering by forbidden drivers */
if (self->priv->forbidden_drivers) {
for (i = 0; self->priv->forbidden_drivers[i]; i++) {
guint j;
for (j = 0; drivers[j]; j++) {
/* If we match a forbidden driver: unsupported */
if (g_str_equal (drivers[j], self->priv->forbidden_drivers[i])) {
mm_dbg ("(%s) [%s] filtered by forbidden drivers",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
}
}
}
/* Implicit filter for forbidden QMI driver */
if (!self->priv->qmi) {
guint j;
for (j = 0; drivers[j]; j++) {
/* If we match the QMI driver: unsupported */
if (g_str_equal (drivers[j], "qmi_wwan")) {
mm_dbg ("(%s) [%s] filtered by implicit QMI driver",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
}
}
/* Implicit filter for forbidden MBIM driver */
if (!self->priv->mbim) {
guint j;
for (j = 0; drivers[j]; j++) {
/* If we match the MBIM driver: unsupported */
if (g_str_equal (drivers[j], "cdc_mbim")) {
mm_dbg ("(%s) [%s] filtered by implicit MBIM driver",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
}
}
}
vendor = mm_device_get_vendor (device);
product = mm_device_get_product (device);
/* The plugin may specify that only some vendor IDs are supported. If that
* is the case, filter by vendor ID. */
if (self->priv->vendor_ids) {
/* If we didn't get any vendor: filtered */
if (!vendor)
vendor_filtered = TRUE;
else {
for (i = 0; self->priv->vendor_ids[i]; i++)
if (vendor == self->priv->vendor_ids[i])
break;
/* If we didn't match any vendor: filtered */
if (!self->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 (self->priv->product_ids) {
/* If we didn't get any product: filtered */
if (!product || !vendor)
product_filtered = TRUE;
else {
for (i = 0; self->priv->product_ids[i].l; i++)
if (vendor == self->priv->product_ids[i].l &&
product == self->priv->product_ids[i].r)
break;
/* If we didn't match any product: filtered */
if (!self->priv->product_ids[i].l)
product_filtered = TRUE;
}
/* When both vendor ids and product ids are given, it may be the case that
* we're allowing a full VID1 and only a subset of another VID2, so try to
* handle that properly. */
if (vendor_filtered && !product_filtered)
vendor_filtered = FALSE;
if (product_filtered && self->priv->vendor_ids && !vendor_filtered)
product_filtered = FALSE;
}
/* If we got filtered by vendor or product IDs; mark it as unsupported only if:
* a) we do not have vendor or product strings to compare with (i.e. plugin
* doesn't have explicit vendor/product strings
* b) the port is NOT an AT port which we can use for AT probing
*/
if ((vendor_filtered || product_filtered) &&
((!self->priv->vendor_strings &&
!self->priv->product_strings &&
!self->priv->forbidden_product_strings) ||
g_str_equal (mm_kernel_device_get_subsystem (port), "net") ||
g_str_has_prefix (mm_kernel_device_get_name (port), "cdc-wdm"))) {
mm_dbg ("(%s) [%s] filtered by vendor/product IDs",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
/* The plugin may specify that some product IDs are not supported. If
* that is the case, filter by forbidden vendor+product ID pair */
if (self->priv->forbidden_product_ids && product && vendor) {
for (i = 0; self->priv->forbidden_product_ids[i].l; i++) {
if (vendor == self->priv->forbidden_product_ids[i].l &&
product == self->priv->forbidden_product_ids[i].r) {
mm_dbg ("(%s) [%s] filtered by forbidden vendor/product IDs",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
}
}
/* Check if we need vendor/product string probing
* Only require these probings if the corresponding filters are given, and:
* 1) if there was no vendor/product ID probing
* 2) if there was vendor/product ID probing but we got filtered
*
* In other words, don't require vendor/product string probing if the plugin
* already had vendor/product ID filters and we actually passed those. */
if ((!self->priv->vendor_ids && !self->priv->product_ids) ||
vendor_filtered ||
product_filtered) {
/* If product strings related filters around, we need to probe for both
* vendor and product strings */
if (self->priv->product_strings ||
self->priv->forbidden_product_strings) {
*need_vendor_probing = TRUE;
*need_product_probing = TRUE;
}
/* If only vendor string filter is needed, only probe for vendor string */
else if (self->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 (self->priv->udev_tags) {
for (i = 0; self->priv->udev_tags[i]; i++) {
/* Check if the port was tagged */
if (mm_kernel_device_get_property_as_boolean (port, self->priv->udev_tags[i]))
break;
}
/* If we didn't match any udev tag: unsupported */
if (!self->priv->udev_tags[i]) {
mm_dbg ("(%s) [%s] filtered by udev tags",
self->priv->name,
mm_kernel_device_get_name (port));
return TRUE;
}
}
return FALSE;
}
/* Returns TRUE if the support check request was filtered out */
static gboolean
apply_post_probing_filters (MMPlugin *self,
MMPortProbeFlag flags,
MMPortProbe *probe)
{
gboolean vendor_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 ((flags & MM_PORT_PROBE_AT_VENDOR) &&
self->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; self->priv->vendor_strings[i]; i++) {
gboolean found;
gchar *casefolded;
casefolded = g_utf8_casefold (self->priv->vendor_strings[i], -1);
found = !!strstr (vendor, casefolded);
g_free (casefolded);
if (found)
break;
}
/* If we didn't match any vendor: filtered */
if (!self->priv->vendor_strings[i])
vendor_filtered = TRUE;
}
if (vendor_filtered) {
if (!self->priv->product_strings) {
mm_dbg ("(%s) [%s] filtered by vendor strings",
self->priv->name,
mm_port_probe_get_port_name (probe));
return TRUE;
}
} else
/* Vendor matched */
return FALSE;
}
/* The plugin may specify that only some vendor+product string pairs are
* supported or unsupported. If that is the case, filter by product
* string */
if ((flags & MM_PORT_PROBE_AT_PRODUCT) &&
(self->priv->product_strings ||
self->priv->forbidden_product_strings)) {
const gchar *vendor;
const gchar *product;
vendor = mm_port_probe_get_vendor (probe);
product = mm_port_probe_get_product (probe);
if (self->priv->product_strings) {
/* If we didn't get any vendor or product: filtered */
if (!vendor || !product) {
mm_dbg ("(%s) [%s] filtered as no vendor/product strings given",
self->priv->name,
mm_port_probe_get_port_name (probe));
return TRUE;
}
else {
for (i = 0; self->priv->product_strings[i].l; i++) {
gboolean found;
gchar *casefolded_vendor;
gchar *casefolded_product;
casefolded_vendor = g_utf8_casefold (self->priv->product_strings[i].l, -1);
casefolded_product = g_utf8_casefold (self->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 (!self->priv->product_strings[i].l) {
mm_dbg ("(%s) [%s] filtered by vendor/product strings",
self->priv->name,
mm_port_probe_get_port_name (probe));
return TRUE;
}
}
}
if (self->priv->forbidden_product_strings && vendor && product) {
for (i = 0; self->priv->forbidden_product_strings[i].l; i++) {
gboolean found;
gchar *casefolded_vendor;
gchar *casefolded_product;
casefolded_vendor = g_utf8_casefold (self->priv->product_strings[i].l, -1);
casefolded_product = g_utf8_casefold (self->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) {
/* If we match a forbidden product: unsupported */
mm_dbg ("(%s) [%s] filtered by forbidden vendor/product strings",
self->priv->name,
mm_port_probe_get_port_name (probe));
return TRUE;
}
}
}
/* Keep on with next filters */
}
/* The plugin may specify that only Icera-based modems are supported.
* If that is the case, filter by allowed Icera support */
if (self->priv->allowed_icera &&
!mm_port_probe_is_icera (probe)) {
/* Unsupported! */
mm_dbg ("(%s) [%s] filtered as modem is not icera",
self->priv->name,
mm_port_probe_get_port_name (probe));
return TRUE;
}
/* The plugin may specify that Icera-based modems are NOT supported.
* If that is the case, filter by forbidden Icera support */
if (self->priv->forbidden_icera &&
mm_port_probe_is_icera (probe)) {
/* Unsupported! */
mm_dbg ("(%s) [%s] filtered as modem is icera",
self->priv->name,
mm_port_probe_get_port_name (probe));
return TRUE;
}
return FALSE;
}
/* Context for the asynchronous probing operation */
typedef struct {
MMPlugin *self;
MMDevice *device;
MMPortProbeFlag flags;
} PortProbeRunContext;
static void
port_probe_run_context_free (PortProbeRunContext *ctx)
{
g_object_unref (ctx->device);
g_object_unref (ctx->self);
g_slice_free (PortProbeRunContext, ctx);
}
static void
port_probe_run_ready (MMPortProbe *probe,
GAsyncResult *probe_result,
GTask *task)
{
GError *error = NULL;
MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNKNOWN;
PortProbeRunContext *ctx;
if (!mm_port_probe_run_finish (probe, probe_result, &error)) {
/* Probing failed saying the port is unsupported. This is not to be
* treated as a generic error, the plugin is just telling us as nicely
* as it can that the port is not supported, so don't warn these cases.
*/
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
goto out;
}
/* Probing failed but the plugin tells us to retry; so we'll defer the
* probing a bit */
if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
result = MM_PLUGIN_SUPPORTS_PORT_DEFER;
goto out;
}
/* For remaining errors, just propagate them */
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Probing succeeded, recover context */
ctx = g_task_get_task_data (task);
/* Apply post probing filters */
if (!apply_post_probing_filters (ctx->self, ctx->flags, probe)) {
/* Port is supported! */
result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED;
/* If we were looking for AT ports, and the port is AT,
* and we were told that only one AT port is expected, cancel AT
* probings in the other available support tasks of the SAME
* device. */
if (ctx->self->priv->single_at &&
ctx->flags & MM_PORT_PROBE_AT &&
mm_port_probe_is_at (probe)) {
GList *l;
for (l = mm_device_peek_port_probe_list (ctx->device); l; l = g_list_next (l)) {
if (l->data != probe)
mm_port_probe_run_cancel_at_probing (MM_PORT_PROBE (l->data));
}
}
} else
/* Filtered by post probing filters */
result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
out:
/* Complete action */
g_clear_error (&error);
g_task_return_int (task, result);
g_object_unref (task);
}
G_STATIC_ASSERT (MM_PLUGIN_SUPPORTS_PORT_UNKNOWN == -1);
MMPluginSupportsResult
mm_plugin_supports_port_finish (MMPlugin *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (MM_IS_PLUGIN (self), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
g_return_val_if_fail (G_IS_TASK (result), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
return (MMPluginSupportsResult) g_task_propagate_int (G_TASK (result), error);
}
void
mm_plugin_supports_port (MMPlugin *self,
MMDevice *device,
MMKernelDevice *port,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMPortProbe *probe = NULL;
GTask *task;
PortProbeRunContext *ctx;
gboolean need_vendor_probing;
gboolean need_product_probing;
MMPortProbeFlag probe_run_flags;
gchar *probe_list_str;
g_return_if_fail (MM_IS_PLUGIN (self));
g_return_if_fail (MM_IS_DEVICE (device));
g_return_if_fail (MM_IS_KERNEL_DEVICE (port));
/* Create new cancellable task */
task = g_task_new (self, cancellable, callback, user_data);
/* Apply filters before launching the probing */
if (apply_pre_probing_filters (self,
device,
port,
&need_vendor_probing,
&need_product_probing)) {
/* Filtered! */
g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
g_object_unref (task);
return;
}
/* Need to launch new probing */
probe = MM_PORT_PROBE (mm_device_peek_port_probe (device, port));
if (!probe) {
/* This may happen if the ports get removed from the device while
* probing is ongoing */
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"(%s) Missing port probe for port (%s/%s)",
self->priv->name,
mm_kernel_device_get_subsystem (port),
mm_kernel_device_get_name (port));
g_object_unref (task);
return;
}
/* Before launching any probing, check if the port is a net device. */
if (g_str_equal (mm_kernel_device_get_subsystem (port), "net")) {
mm_dbg ("(%s) [%s] probing deferred until result suggested",
self->priv->name, mm_kernel_device_get_name (port));
g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED);
g_object_unref (task);
return;
}
/* Build flags depending on what probing needed */
probe_run_flags = MM_PORT_PROBE_NONE;
if (!g_str_has_prefix (mm_kernel_device_get_name (port), "cdc-wdm")) {
/* Serial ports... */
if (self->priv->at)
probe_run_flags |= MM_PORT_PROBE_AT;
else if (self->priv->single_at)
probe_run_flags |= MM_PORT_PROBE_AT;
if (self->priv->qcdm)
probe_run_flags |= MM_PORT_PROBE_QCDM;
} else {
/* cdc-wdm ports... */
if (self->priv->qmi && !g_strcmp0 (mm_kernel_device_get_driver (port), "qmi_wwan"))
probe_run_flags |= MM_PORT_PROBE_QMI;
else if (self->priv->mbim && !g_strcmp0 (mm_kernel_device_get_driver (port), "cdc_mbim"))
probe_run_flags |= MM_PORT_PROBE_MBIM;
else
probe_run_flags |= MM_PORT_PROBE_AT;
}
/* For potential AT ports, check for more things */
if (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 (self->priv->icera_probe || self->priv->allowed_icera || self->priv->forbidden_icera)
probe_run_flags |= MM_PORT_PROBE_AT_ICERA;
}
/* If no explicit probing was required, just request to grab it without probing anything.
* This may happen, e.g. with cdc-wdm ports which do not need QMI/MBIM probing. */
if (probe_run_flags == MM_PORT_PROBE_NONE) {
g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED);
g_object_unref (task);
return;
}
/* If a modem is already available and the plugin says that only one AT port is
* expected, check if we alredy got the single AT port. And if so, we know this
* port being probed won't be AT. */
if (self->priv->single_at &&
mm_port_probe_list_has_at_port (mm_device_peek_port_probe_list (device)) &&
!mm_port_probe_is_at (probe)) {
mm_dbg ("(%s) [%s] not setting up AT probing tasks: "
"modem already has the expected single AT port",
self->priv->name,
mm_kernel_device_get_name (port));
/* Assuming it won't be an AT port. We still run the probe anyway, in
* case we need to check for other port types (e.g. QCDM) */
mm_port_probe_set_result_at (probe, FALSE);
}
/* Setup async call context */
ctx = g_slice_new0 (PortProbeRunContext);
ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
ctx->flags = probe_run_flags;
/* Store context in task */
g_task_set_task_data (task, ctx, (GDestroyNotify) port_probe_run_context_free);
/* Launch the probe */
probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
mm_dbg ("(%s) [%s] probe required: '%s'",
self->priv->name,
mm_kernel_device_get_name (port),
probe_list_str);
g_free (probe_list_str);
mm_port_probe_run (probe,
ctx->flags,
self->priv->send_delay,
self->priv->remove_echo,
self->priv->send_lf,
self->priv->custom_at_probe,
self->priv->custom_init,
cancellable,
(GAsyncReadyCallback) port_probe_run_ready,
task);
}
/*****************************************************************************/
MMPluginSupportsHint
mm_plugin_discard_port_early (MMPlugin *self,
MMDevice *device,
MMKernelDevice *port)
{
gboolean need_vendor_probing = FALSE;
gboolean need_product_probing = FALSE;
/* If fully filtered by pre-probing filters, port unsupported */
if (apply_pre_probing_filters (self,
device,
port,
&need_vendor_probing,
&need_product_probing))
return MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED;
/* If there are no post-probing filters, this plugin is the only one (except
* for the generic one) which will grab the port */
if (!HAS_POST_PROBING_FILTERS (self))
return MM_PLUGIN_SUPPORTS_HINT_SUPPORTED;
/* If no vendor/product probing needed, plugin is likely supported */
if (!need_vendor_probing && !need_product_probing)
return MM_PLUGIN_SUPPORTS_HINT_LIKELY;
/* If vendor/product probing is needed, plugin may be supported */
return MM_PLUGIN_SUPPORTS_HINT_MAYBE;
}
/*****************************************************************************/
MMBaseModem *
mm_plugin_create_modem (MMPlugin *self,
MMDevice *device,
GError **error)
{
MMBaseModem *modem;
GList *port_probes = NULL;
const gchar **virtual_ports = NULL;
if (!mm_device_is_virtual (device))
port_probes = mm_device_peek_port_probe_list (device);
else
virtual_ports = mm_device_virtual_peek_ports (device);
/* Let the plugin create the modem from the port probe results */
modem = MM_PLUGIN_GET_CLASS (self)->create_modem (MM_PLUGIN (self),
mm_device_get_uid (device),
mm_device_get_drivers (device),
mm_device_get_vendor (device),
mm_device_get_product (device),
port_probes,
error);
if (!modem)
return NULL;
mm_base_modem_set_hotplugged (modem, mm_device_get_hotplugged (device));
if (port_probes) {
GList *l;
/* Grab each port */
for (l = port_probes; l; l = g_list_next (l)) {
GError *inner_error = NULL;
MMPortProbe *probe = MM_PORT_PROBE (l->data);
gboolean grabbed;
/* If grabbing a port fails, just warn. We'll decide if the modem is
* valid or not when all ports get organized */
/* We apply again the subsystem filter, as the port may have been
* probed and accepted by the generic plugin, which is overwritten
* by the specific one when needed. */
if (apply_subsystem_filter (self, mm_port_probe_peek_port (probe))) {
grabbed = FALSE;
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"unsupported subsystem: '%s'",
mm_port_probe_get_port_subsys (probe));
}
/* Ports that are explicitly blacklisted will be grabbed as ignored */
else if (mm_port_probe_is_ignored (probe)) {
mm_dbg ("(%s/%s): port is blacklisted",
mm_port_probe_get_port_subsys (probe),
mm_port_probe_get_port_name (probe));
grabbed = mm_base_modem_grab_port (modem,
mm_port_probe_peek_port (probe),
MM_PORT_TYPE_IGNORED,
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error);
}
#if !defined WITH_QMI
else if (mm_port_probe_get_port_type (probe) == MM_PORT_TYPE_NET &&
!g_strcmp0 (mm_kernel_device_get_driver (mm_port_probe_peek_port (probe)), "qmi_wwan")) {
/* Try to generically grab the port, but flagged as ignored */
grabbed = mm_base_modem_grab_port (modem,
mm_port_probe_peek_port (probe),
MM_PORT_TYPE_IGNORED,
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error);
}
#endif
#if !defined WITH_MBIM
else if (mm_port_probe_get_port_type (probe) == MM_PORT_TYPE_NET &&
!g_strcmp0 (mm_kernel_device_get_driver (mm_port_probe_peek_port (probe)), "cdc_mbim")) {
/* Try to generically grab the port, but flagged as ignored */
grabbed = mm_base_modem_grab_port (modem,
mm_port_probe_peek_port (probe),
MM_PORT_TYPE_IGNORED,
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error);
}
#endif
else if (MM_PLUGIN_GET_CLASS (self)->grab_port)
grabbed = MM_PLUGIN_GET_CLASS (self)->grab_port (MM_PLUGIN (self),
modem,
probe,
&inner_error);
else
grabbed = mm_base_modem_grab_port (modem,
mm_port_probe_peek_port (probe),
mm_port_probe_get_port_type (probe),
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error);
if (!grabbed) {
mm_warn ("Could not grab port (%s/%s): '%s'",
mm_port_probe_get_port_subsys (MM_PORT_PROBE (l->data)),
mm_port_probe_get_port_name (MM_PORT_PROBE (l->data)),
inner_error ? inner_error->message : "unknown error");
g_clear_error (&inner_error);
}
}
} else if (virtual_ports) {
guint i;
for (i = 0; virtual_ports[i]; i++) {
GError *inner_error = NULL;
MMKernelDevice *kernel_device;
MMKernelEventProperties *properties;
properties = mm_kernel_event_properties_new ();
mm_kernel_event_properties_set_action (properties, "add");
mm_kernel_event_properties_set_subsystem (properties, "virtual");
mm_kernel_event_properties_set_name (properties, virtual_ports[i]);
/* Give an empty set of rules, because we don't want them to be
* loaded from the udev rules path (as there may not be any
* installed yet). */
kernel_device = mm_kernel_device_generic_new_with_rules (properties, NULL, &inner_error);
if (!kernel_device) {
mm_warn ("Could not grab port (virtual/%s): '%s'",
virtual_ports[i],
inner_error ? inner_error->message : "unknown error");
g_clear_error (&inner_error);
} else if (!mm_base_modem_grab_port (modem,
kernel_device,
MM_PORT_TYPE_AT,
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error)) {
mm_warn ("Could not grab port (virtual/%s): '%s'",
virtual_ports[i],
inner_error ? inner_error->message : "unknown error");
g_clear_error (&inner_error);
}
if (kernel_device)
g_object_unref (kernel_device);
g_object_unref (properties);
}
}
/* If organizing ports fails, consider the modem invalid */
if (!mm_base_modem_organize_ports (modem, error))
g_clear_object (&modem);
return modem;
}
/*****************************************************************************/
static void
mm_plugin_init (MMPlugin *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_PLUGIN,
MMPluginPrivate);
/* Defaults */
self->priv->send_delay = 100000;
self->priv->remove_echo = TRUE;
self->priv->send_lf = FALSE;
}
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MMPlugin *self = MM_PLUGIN (object);
switch (prop_id) {
case PROP_NAME:
/* Construct only */
self->priv->name = g_value_dup_string (value);
break;
case PROP_ALLOWED_SUBSYSTEMS:
/* Construct only */
self->priv->subsystems = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_DRIVERS:
/* Construct only */
self->priv->drivers = g_value_dup_boxed (value);
break;
case PROP_FORBIDDEN_DRIVERS:
/* Construct only */
self->priv->forbidden_drivers = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_VENDOR_IDS:
/* Construct only */
self->priv->vendor_ids = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_PRODUCT_IDS:
/* Construct only */
self->priv->product_ids = g_value_dup_boxed (value);
break;
case PROP_FORBIDDEN_PRODUCT_IDS:
/* Construct only */
self->priv->forbidden_product_ids = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_VENDOR_STRINGS:
/* Construct only */
self->priv->vendor_strings = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_PRODUCT_STRINGS:
/* Construct only */
self->priv->product_strings = g_value_dup_boxed (value);
break;
case PROP_FORBIDDEN_PRODUCT_STRINGS:
/* Construct only */
self->priv->forbidden_product_strings = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_UDEV_TAGS:
/* Construct only */
self->priv->udev_tags = g_value_dup_boxed (value);
break;
case PROP_ALLOWED_AT:
/* Construct only */
self->priv->at = g_value_get_boolean (value);
break;
case PROP_ALLOWED_SINGLE_AT:
/* Construct only */
self->priv->single_at = g_value_get_boolean (value);
break;
case PROP_ALLOWED_QCDM:
/* Construct only */
self->priv->qcdm = g_value_get_boolean (value);
break;
case PROP_ALLOWED_QMI:
/* Construct only */
self->priv->qmi = g_value_get_boolean (value);
break;
case PROP_ALLOWED_MBIM:
/* Construct only */
self->priv->mbim = g_value_get_boolean (value);
break;
case PROP_ICERA_PROBE:
/* Construct only */
self->priv->icera_probe = g_value_get_boolean (value);
break;
case PROP_ALLOWED_ICERA:
/* Construct only */
self->priv->allowed_icera = g_value_get_boolean (value);
break;
case PROP_FORBIDDEN_ICERA:
/* Construct only */
self->priv->forbidden_icera = g_value_get_boolean (value);
break;
case PROP_CUSTOM_AT_PROBE:
/* Construct only */
self->priv->custom_at_probe = g_value_dup_boxed (value);
break;
case PROP_CUSTOM_INIT:
/* Construct only */
self->priv->custom_init = g_value_dup_boxed (value);
break;
case PROP_SEND_DELAY:
/* Construct only */
self->priv->send_delay = (guint64)g_value_get_uint64 (value);
break;
case PROP_REMOVE_ECHO:
/* Construct only */
self->priv->remove_echo = g_value_get_boolean (value);
break;
case PROP_SEND_LF:
/* Construct only */
self->priv->send_lf = g_value_get_boolean (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)
{
MMPlugin *self = MM_PLUGIN (object);
switch (prop_id) {
case PROP_NAME:
g_value_set_string (value, self->priv->name);
break;
case PROP_ALLOWED_SUBSYSTEMS:
g_value_set_boxed (value, self->priv->subsystems);
break;
case PROP_ALLOWED_DRIVERS:
g_value_set_boxed (value, self->priv->drivers);
break;
case PROP_FORBIDDEN_DRIVERS:
g_value_set_boxed (value, self->priv->forbidden_drivers);
break;
case PROP_ALLOWED_VENDOR_IDS:
g_value_set_boxed (value, self->priv->vendor_ids);
break;
case PROP_ALLOWED_PRODUCT_IDS:
g_value_set_boxed (value, self->priv->product_ids);
break;
case PROP_FORBIDDEN_PRODUCT_IDS:
g_value_set_boxed (value, self->priv->forbidden_product_ids);
break;
case PROP_ALLOWED_VENDOR_STRINGS:
g_value_set_boxed (value, self->priv->vendor_strings);
break;
case PROP_ALLOWED_PRODUCT_STRINGS:
g_value_set_boxed (value, self->priv->product_strings);
break;
case PROP_FORBIDDEN_PRODUCT_STRINGS:
g_value_set_boxed (value, self->priv->forbidden_product_strings);
break;
case PROP_ALLOWED_AT:
g_value_set_boolean (value, self->priv->at);
break;
case PROP_ALLOWED_SINGLE_AT:
g_value_set_boolean (value, self->priv->single_at);
break;
case PROP_ALLOWED_QCDM:
g_value_set_boolean (value, self->priv->qcdm);
break;
case PROP_ALLOWED_QMI:
g_value_set_boolean (value, self->priv->qmi);
break;
case PROP_ALLOWED_MBIM:
g_value_set_boolean (value, self->priv->mbim);
break;
case PROP_ALLOWED_UDEV_TAGS:
g_value_set_boxed (value, self->priv->udev_tags);
break;
case PROP_ICERA_PROBE:
g_value_set_boolean (value, self->priv->icera_probe);
break;
case PROP_ALLOWED_ICERA:
g_value_set_boolean (value, self->priv->allowed_icera);
break;
case PROP_FORBIDDEN_ICERA:
g_value_set_boolean (value, self->priv->forbidden_icera);
break;
case PROP_CUSTOM_AT_PROBE:
g_value_set_boxed (value, self->priv->custom_at_probe);
break;
case PROP_CUSTOM_INIT:
g_value_set_boxed (value, self->priv->custom_init);
break;
case PROP_SEND_DELAY:
g_value_set_uint64 (value, self->priv->send_delay);
break;
case PROP_REMOVE_ECHO:
g_value_set_boolean (value, self->priv->remove_echo);
break;
case PROP_SEND_LF:
g_value_set_boolean (value, self->priv->send_lf);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
finalize (GObject *object)
{
MMPlugin *self = MM_PLUGIN (object);
g_free (self->priv->name);
#define _g_boxed_free0(t,p) if (p) g_boxed_free (t, p)
_g_boxed_free0 (G_TYPE_STRV, self->priv->subsystems);
_g_boxed_free0 (G_TYPE_STRV, self->priv->drivers);
_g_boxed_free0 (G_TYPE_STRV, self->priv->forbidden_drivers);
_g_boxed_free0 (MM_TYPE_UINT16_ARRAY, self->priv->vendor_ids);
_g_boxed_free0 (MM_TYPE_UINT16_PAIR_ARRAY, self->priv->product_ids);
_g_boxed_free0 (MM_TYPE_UINT16_PAIR_ARRAY, self->priv->forbidden_product_ids);
_g_boxed_free0 (G_TYPE_STRV, self->priv->udev_tags);
_g_boxed_free0 (G_TYPE_STRV, self->priv->vendor_strings);
_g_boxed_free0 (MM_TYPE_STR_PAIR_ARRAY, self->priv->product_strings);
_g_boxed_free0 (MM_TYPE_STR_PAIR_ARRAY, self->priv->forbidden_product_strings);
_g_boxed_free0 (MM_TYPE_POINTER_ARRAY, self->priv->custom_at_probe);
_g_boxed_free0 (MM_TYPE_ASYNC_METHOD, self->priv->custom_init);
#undef _g_boxed_free0
G_OBJECT_CLASS (mm_plugin_parent_class)->finalize (object);
}
static void
mm_plugin_class_init (MMPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMPluginPrivate));
/* 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_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_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_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_FORBIDDEN_DRIVERS,
g_param_spec_boxed (MM_PLUGIN_FORBIDDEN_DRIVERS,
"Forbidden drivers",
"List of drivers this plugin cannot 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_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_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_FORBIDDEN_PRODUCT_IDS,
g_param_spec_boxed (MM_PLUGIN_FORBIDDEN_PRODUCT_IDS,
"Forbidden product IDs",
"List of vendor+product ID pairs this plugin cannot 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_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_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_FORBIDDEN_PRODUCT_STRINGS,
g_param_spec_boxed (MM_PLUGIN_FORBIDDEN_PRODUCT_STRINGS,
"Forbidden product strings",
"List of vendor+product string pairs this plugin cannot 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_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_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_SINGLE_AT,
g_param_spec_boolean (MM_PLUGIN_ALLOWED_SINGLE_AT,
"Allowed single AT",
"Whether just a single AT port is 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_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_ALLOWED_QMI,
g_param_spec_boolean (MM_PLUGIN_ALLOWED_QMI,
"Allowed QMI",
"Whether QMI ports are allowed in this plugin",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_ALLOWED_MBIM,
g_param_spec_boolean (MM_PLUGIN_ALLOWED_MBIM,
"Allowed MBIM",
"Whether MBIM ports are allowed in this plugin",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_ICERA_PROBE,
g_param_spec_boolean (MM_PLUGIN_ICERA_PROBE,
"Icera probe",
"Request to probe for Icera support.",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_ALLOWED_ICERA,
g_param_spec_boolean (MM_PLUGIN_ALLOWED_ICERA,
"Allowed Icera",
"Whether Icera support is allowed in this plugin.",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_FORBIDDEN_ICERA,
g_param_spec_boolean (MM_PLUGIN_FORBIDDEN_ICERA,
"Allowed Icera",
"Whether Icera support is forbidden in this plugin.",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_CUSTOM_AT_PROBE,
g_param_spec_boxed (MM_PLUGIN_CUSTOM_AT_PROBE,
"Custom AT Probe",
"Custom set of commands to probe for AT support, "
"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_CUSTOM_INIT,
g_param_spec_boxed (MM_PLUGIN_CUSTOM_INIT,
"Custom initialization",
"Asynchronous method setup which contains the "
"custom initializations this plugin needs.",
MM_TYPE_ASYNC_METHOD,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_SEND_DELAY,
g_param_spec_uint64 (MM_PLUGIN_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));
g_object_class_install_property
(object_class, PROP_REMOVE_ECHO,
g_param_spec_boolean (MM_PLUGIN_REMOVE_ECHO,
"Remove echo",
"Remove echo out of the AT responses",
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_SEND_LF,
g_param_spec_boolean (MM_PLUGIN_SEND_LF,
"Send LF",
"Send line-feed at the end of each AT command sent",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}