[PATCH 04/10] api: new ReportKernelEvent() api method to report kernel device addition/removals
Dan Williams
dcbw at redhat.com
Fri Aug 19 19:55:42 UTC 2016
On Sat, 2016-08-06 at 15:03 +0200, Aleksander Morgado wrote:
> The implementation allows different parameters being reported in the
> kernel
> event, but only 3 of them are mandatory: 'action', 'name' and
> 'subsystem'. Once
> these three parameters are received, ModemManager will load the
> remaining kernel
> device information directly from sysfs.
>
> E.g.:
> $ sudo mmcli --report-kernel-event=" \
> action=add, \
> name=wwan0, \
> subsystem=net
>
> The 'physdev-uid' optional parameter may be given to bind together
> multiple
> ports of the same device under a common known name.
>
> The 'physdev-vid', 'physdev-pid' and 'driver' optional parameters may
> be given
> to force a given device to be managed as if it was some other device.
> This
> doesn't have any hard use case in real life, unless for testing
> devices (e.g. so
> that different filters get applied during runtime).
>
> E.g.:
> $ sudo mmcli --report-kernel-event=" \
> action=add, \
> name=wwan0, \
> subsystem=net, \
> physdev-uid=usb-modem-rear-usb-port, \
> physdev-vid=1199, \
> physdev-pid=68a2, \
> driver=qmi_wwan"
In the API docs it talks about USB vid/pid, but MM does support at
least one PCI-native device; the Option Nozomi. SDIO also uses 16-bit
vid/pid, though I'm not sure why anyone would use SDIO to hook up a
WWAN modem :) Maybe just say it's a USB or PCI vid/pid for now, but
could be used for more in the future?
Which means I'm not sure it should be a 'q'. Maybe just an 's' with
guint16 validation for now?
Also, would be nice to have some unit tests for the udev rules file
parsing code...
Dan
> The 'candidate' optional parameter may be given to force treating as
> candidate
> a given port that otherwise would have been ignored by the automatic
> rules
> applied by ModemManager.
>
> E.g.:
> $ sudo mmcli --report-kernel-event=" \
> action=add, \
> name=/dev/ttyS0, \
> subsystem=tty,
> candidate=yes
> ---
> cli/mmcli-manager.c | 77 +-
> docs/reference/libmm-glib/libmm-glib-docs.xml | 1 +
> docs/reference/libmm-glib/libmm-glib-sections.txt | 49 +-
> introspection/org.freedesktop.ModemManager1.xml | 121 +++
> libmm-glib/Makefile.am | 3 +
> libmm-glib/libmm-glib.h | 1 +
> libmm-glib/mm-common-helpers.c | 43 +
> libmm-glib/mm-common-helpers.h | 2 +
> libmm-glib/mm-kernel-event-properties.c | 689
> ++++++++++++++
> libmm-glib/mm-kernel-event-properties.h | 113 +++
> libmm-glib/mm-manager.c | 124 +++
> libmm-glib/mm-manager.h | 14 +
> src/Makefile.am | 6 +
> src/kerneldevice/mm-kernel-device-generic.c | 1010
> +++++++++++++++++++++
> src/kerneldevice/mm-kernel-device-generic.h | 51 ++
> src/mm-base-manager.c | 147 ++-
> 16 files changed, 2444 insertions(+), 7 deletions(-)
> create mode 100644 libmm-glib/mm-kernel-event-properties.c
> create mode 100644 libmm-glib/mm-kernel-event-properties.h
> create mode 100644 src/kerneldevice/mm-kernel-device-generic.c
> create mode 100644 src/kerneldevice/mm-kernel-device-generic.h
>
> diff --git a/cli/mmcli-manager.c b/cli/mmcli-manager.c
> index 9960412..d78734f 100644
> --- a/cli/mmcli-manager.c
> +++ b/cli/mmcli-manager.c
> @@ -47,6 +47,7 @@ static gboolean list_modems_flag;
> static gboolean monitor_modems_flag;
> static gboolean scan_modems_flag;
> static gchar *set_logging_str;
> +static gchar *report_kernel_event_str;
>
> static GOptionEntry entries[] = {
> { "set-logging", 'G', 0, G_OPTION_ARG_STRING, &set_logging_str,
> @@ -65,6 +66,10 @@ static GOptionEntry entries[] = {
> "Request to re-scan looking for modems",
> NULL
> },
> + { "report-kernel-event", 'K', 0, G_OPTION_ARG_STRING,
> &report_kernel_event_str,
> + "Report kernel event",
> + "[\"key=value,...\"]"
> + },
> { NULL }
> };
>
> @@ -96,7 +101,8 @@ mmcli_manager_options_enabled (void)
> n_actions = (list_modems_flag +
> monitor_modems_flag +
> scan_modems_flag +
> - !!set_logging_str);
> + !!set_logging_str +
> + !!report_kernel_event_str);
>
> if (n_actions > 1) {
> g_printerr ("error: too many manager actions requested\n");
> @@ -130,6 +136,47 @@ mmcli_manager_shutdown (void)
> }
>
> static void
> +report_kernel_event_process_reply (gboolean result,
> + const GError *error)
> +{
> + if (!result) {
> + g_printerr ("error: couldn't report kernel event: '%s'\n",
> + error ? error->message : "unknown error");
> + exit (EXIT_FAILURE);
> + }
> +
> + g_print ("successfully reported kernel event\n");
> +}
> +
> +static void
> +report_kernel_event_ready (MMManager *manager,
> + GAsyncResult *result)
> +{
> + gboolean operation_result;
> + GError *error = NULL;
> +
> + operation_result = mm_manager_report_kernel_event_finish
> (manager, result, &error);
> + report_kernel_event_process_reply (operation_result, error);
> +
> + mmcli_async_operation_done ();
> +}
> +
> +static MMKernelEventProperties *
> +build_kernel_event_properties_from_input (const gchar
> *properties_string)
> +{
> + GError *error = NULL;
> + MMKernelEventProperties *properties;
> +
> + properties = mm_kernel_event_properties_new_from_string
> (properties_string, &error);
> + if (!properties) {
> + g_printerr ("error: cannot parse properties string: '%s'\n",
> error->message);
> + exit (EXIT_FAILURE);
> + }
> +
> + return properties;
> +}
> +
> +static void
> set_logging_process_reply (gboolean result,
> const GError *error)
> {
> @@ -276,6 +323,20 @@ get_manager_ready (GObject *source,
> return;
> }
>
> + /* Request to report kernel event? */
> + if (report_kernel_event_str) {
> + MMKernelEventProperties *properties;
> +
> + properties = build_kernel_event_properties_from_input
> (report_kernel_event_str);
> + mm_manager_report_kernel_event (ctx->manager,
> + properties,
> + ctx->cancellable,
> + (GAsyncReadyCallback)report_
> kernel_event_ready,
> + NULL);
> + g_object_unref (properties);
> + return;
> + }
> +
> /* Request to monitor modems? */
> if (monitor_modems_flag) {
> g_signal_connect (ctx->manager,
> @@ -362,6 +423,20 @@ mmcli_manager_run_synchronous (GDBusConnection
> *connection)
> return;
> }
>
> + /* Request to report kernel event? */
> + if (report_kernel_event_str) {
> + MMKernelEventProperties *properties;
> + gboolean result;
> +
> + properties = build_kernel_event_properties_from_input
> (report_kernel_event_str);
> + result = mm_manager_report_kernel_event_sync (ctx->manager,
> + properties,
> + NULL,
> + &error);
> + report_kernel_event_process_reply (result, error);
> + return;
> + }
> +
> /* Request to list modems? */
> if (list_modems_flag) {
> list_current_modems (ctx->manager);
> diff --git a/docs/reference/libmm-glib/libmm-glib-docs.xml
> b/docs/reference/libmm-glib/libmm-glib-docs.xml
> index 4e1d7fe..f611b72 100644
> --- a/docs/reference/libmm-glib/libmm-glib-docs.xml
> +++ b/docs/reference/libmm-glib/libmm-glib-docs.xml
> @@ -72,6 +72,7 @@
> <chapter>
> <title>The Manager object</title>
> <xi:include href="xml/mm-manager.xml"/>
> + <xi:include href="xml/mm-kernel-event-properties.xml"/>
> </chapter>
>
> <chapter>
> diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt
> b/docs/reference/libmm-glib/libmm-glib-sections.txt
> index 27a98cd..804a032 100644
> --- a/docs/reference/libmm-glib/libmm-glib-sections.txt
> +++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
> @@ -20,6 +20,9 @@ mm_manager_scan_devices_sync
> mm_manager_set_logging
> mm_manager_set_logging_finish
> mm_manager_set_logging_sync
> +mm_manager_report_kernel_event
> +mm_manager_report_kernel_event_finish
> +mm_manager_report_kernel_event_sync
> <SUBSECTION Standard>
> MMManagerClass
> MMManagerPrivate
> @@ -33,6 +36,46 @@ mm_manager_get_type
> </SECTION>
>
> <SECTION>
> +<FILE>mm-kernel-event-properties</FILE>
> +<TITLE>MMKernelEventProperties</TITLE>
> +MMKernelEventProperties
> +<SUBSECTION New>
> +mm_kernel_event_properties_new
> +mm_kernel_event_properties_dup
> +<SUBSECTION GettersSetters>
> +mm_kernel_event_properties_get_action
> +mm_kernel_event_properties_set_action
> +mm_kernel_event_properties_get_candidate
> +mm_kernel_event_properties_set_candidate
> +mm_kernel_event_properties_get_driver
> +mm_kernel_event_properties_set_driver
> +mm_kernel_event_properties_get_name
> +mm_kernel_event_properties_set_name
> +mm_kernel_event_properties_get_physdev_pid
> +mm_kernel_event_properties_set_physdev_pid
> +mm_kernel_event_properties_get_physdev_uid
> +mm_kernel_event_properties_set_physdev_uid
> +mm_kernel_event_properties_get_physdev_vid
> +mm_kernel_event_properties_set_physdev_vid
> +mm_kernel_event_properties_get_subsystem
> +mm_kernel_event_properties_set_subsystem
> +<SUBSECTION Private>
> +mm_kernel_event_properties_get_dictionary
> +mm_kernel_event_properties_new_from_dictionary
> +mm_kernel_event_properties_new_from_string
> +<SUBSECTION Standard>
> +MMKernelEventPropertiesClass
> +MMKernelEventPropertiesPrivate
> +MM_IS_KERNEL_EVENT_PROPERTIES
> +MM_IS_KERNEL_EVENT_PROPERTIES_CLASS
> +MM_KERNEL_EVENT_PROPERTIES
> +MM_KERNEL_EVENT_PROPERTIES_CLASS
> +MM_KERNEL_EVENT_PROPERTIES_GET_CLASS
> +MM_TYPE_KERNEL_EVENT_PROPERTIES
> +mm_kernel_event_properties_get_type
> +</SECTION>
> +
> +<SECTION>
> <FILE>mm-object</FILE>
> <TITLE>MMObject</TITLE>
> MMObject
> @@ -295,7 +338,6 @@ mm_modem_3gpp_ussd_get_type
> <FILE>mm-cdma-manual-activation-properties</FILE>
> <TITLE>MMCdmaManualActivationProperties</TITLE>
> MMCdmaManualActivationProperties
> -
> <SUBSECTION Methods>
> mm_cdma_manual_activation_properties_new
> mm_cdma_manual_activation_properties_get_spc
> @@ -896,7 +938,6 @@ MMModemVoice
> <SUBSECTION Getters>
> mm_modem_voice_get_path
> mm_modem_voice_dup_path
> -
> <SUBSECTION Methods>
> mm_modem_voice_create_call
> mm_modem_voice_create_call_finish
> @@ -1555,10 +1596,14 @@
> mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync
> mm_gdbus_org_freedesktop_modem_manager1_call_set_logging
> mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish
> mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync
> +mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event
> +mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_fin
> ish
> +mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_syn
> c
> <SUBSECTION Private>
> mm_gdbus_org_freedesktop_modem_manager1_override_properties
> mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices
> mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging
> +mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event
> mm_gdbus_org_freedesktop_modem_manager1_interface_info
> <SUBSECTION Standard>
> MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1
> diff --git a/introspection/org.freedesktop.ModemManager1.xml
> b/introspection/org.freedesktop.ModemManager1.xml
> index 2ecd026..32c896b 100644
> --- a/introspection/org.freedesktop.ModemManager1.xml
> +++ b/introspection/org.freedesktop.ModemManager1.xml
> @@ -37,5 +37,126 @@
> <arg name="level" type="s" direction="in" />
> </method>
>
> + <!--
> + ReportKernelEvent:
> + @properties: event properties.
> +
> + Reports a kernel event to ModemManager.
> +
> + This method is only available if udev is not being used to
> report kernel
> + events.
> +
> + The @properties dictionary is composed of key/value string
> pairs. The
> + possible keys are:
> +
> + <variablelist>
> +
> + <varlistentry><term><literal>action</literal></term>
> + <listitem>
> + <para>
> + The type of action, given as a string value
> (signature
> + <literal>"s"</literal>).
> + This parameter is MANDATORY.
> + </para>
> + <variablelist>
> + <varlistentry><term><literal>add</literal></term>
> + <listitem>
> + A new kernel device has been added.
> + </listitem>
> + </varlistentry>
> + <varlistentry><term><literal>remove</literal></term>
> + <listitem>
> + An existing kernel device has been removed.
> + </listitem>
> + </varlistentry>
> + </variablelist>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>name</literal></term>
> + <listitem>
> + <para>
> + The device name, given as a string value (signature
> + <literal>"s"</literal>).
> + This parameter is MANDATORY.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>subsystem</literal></term>
> + <listitem>
> + <para>
> + The device subsystem, given as a string value
> (signature
> + <literal>"s"</literal>).
> + This parameter is MANDATORY.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>driver</literal></term>
> + <listitem>
> + <para>
> + The device driver, given as a string value
> (signature
> + <literal>"s"</literal>).
> + This parameter is OPTIONAL, if not given it will be
> retrieved
> + from sysfs.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>physdev-uid</literal></term>
> + <listitem>
> + <para>
> + The unique ID of the physical device, given as a
> string value
> + (signature <literal>"s"</literal>).
> + This parameter is OPTIONAL, if not given the sysfs
> path of the
> + physical device will be used. This parameter must be
> the same
> + for all devices exposed by the same physical device.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>physdev-vid</literal></term>
> + <listitem>
> + <para>
> + The USB vendor ID of the physical device, given as a
> 16 bit
> + unsigned integer (signature <literal>"q"</literal>.
> + This parameter is OPTIONAL, if not given it will be
> retrieved
> + from sysfs. This parameter may be used to override
> the real one
> + exposed by the device.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>physdev-pid</literal></term>
> + <listitem>
> + <para>
> + The USB product ID of the physical device, given as
> a 16 bit
> + unsigned integer (signature <literal>"q"</literal>.
> + This parameter is OPTIONAL, if not given it will be
> retrieved
> + from sysfs. This parameter may be used to override
> the real one
> + exposed by the device.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry><term><literal>candidate</literal></term>
> + <listitem>
> + <para>
> + Flag to force treating the device as a candidate,
> despite other
> + rules that ModemManager may have, given as a boolean
> value
> + (signature <literal>"b"</literal>).
> + This parameter is OPTIONAL, and not specifying it
> has the same
> + effect as setting it to false.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + </variablelist>
> + -->
> + <method name="ReportKernelEvent">
> + <arg name="properties" type="a{sv}" direction="in" />
> + </method>
> +
> </interface>
> </node>
> diff --git a/libmm-glib/Makefile.am b/libmm-glib/Makefile.am
> index e257d6f..64bca40 100644
> --- a/libmm-glib/Makefile.am
> +++ b/libmm-glib/Makefile.am
> @@ -81,6 +81,8 @@ libmm_glib_la_SOURCES = \
> mm-cdma-manual-activation-properties.c \
> mm-signal.h \
> mm-signal.c \
> + mm-kernel-event-properties.h \
> + mm-kernel-event-properties.c \
> $(NULL)
>
> libmm_glib_la_CPPFLAGS = \
> @@ -149,6 +151,7 @@ include_HEADERS = \
> mm-firmware-properties.h \
> mm-cdma-manual-activation-properties.h \
> mm-signal.h \
> + mm-kernel-event-properties.h \
> $(NULL)
>
> CLEANFILES =
> diff --git a/libmm-glib/libmm-glib.h b/libmm-glib/libmm-glib.h
> index fa4c7e2..53f0a19 100644
> --- a/libmm-glib/libmm-glib.h
> +++ b/libmm-glib/libmm-glib.h
> @@ -72,6 +72,7 @@
> #include <mm-firmware-properties.h>
> #include <mm-cdma-manual-activation-properties.h>
> #include <mm-signal.h>
> +#include <mm-kernel-event-properties.h>
>
> /* generated */
> #include <mm-errors-types.h>
> diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-
> helpers.c
> index c1ddd89..2af5850 100644
> --- a/libmm-glib/mm-common-helpers.c
> +++ b/libmm-glib/mm-common-helpers.c
> @@ -1385,6 +1385,49 @@ mm_get_uint_from_str (const gchar *str,
> return FALSE;
> }
>
> +/**
> + * mm_get_uint_from_hex_str:
> + * @str: the hex string to convert to an unsigned int
> + * @out: on success, the number
> + *
> + * Converts a string to an unsigned number. All characters in the
> string
> + * MUST be valid hexadecimal digits (0-9, A-F, a-f), otherwise FALSE
> is
> + * returned.
> + *
> + * An optional "0x" prefix may be given in @str.
> + *
> + * Returns: %TRUE if the string was converted, %FALSE if it was not
> or if it
> + * did not contain only digits.
> + */
> +gboolean
> +mm_get_uint_from_hex_str (const gchar *str,
> + guint *out)
> +{
> + gulong num;
> +
> + if (!str)
> + return FALSE;
> +
> + if (g_str_has_prefix (str, "0x"))
> + str = &str[2];
> +
> + if (!str[0])
> + return FALSE;
> +
> + for (num = 0; str[num]; num++) {
> + if (!g_ascii_isxdigit (str[num]))
> + return FALSE;
> + }
> +
> + errno = 0;
> + num = strtoul (str, NULL, 16);
> + if (!errno && num <= G_MAXUINT) {
> + *out = (guint)num;
> + return TRUE;
> + }
> + return FALSE;
> +}
> +
> gboolean
> mm_get_uint_from_match_info (GMatchInfo *match_info,
> guint32 match_index,
> diff --git a/libmm-glib/mm-common-helpers.h b/libmm-glib/mm-common-
> helpers.h
> index c65d3b9..ab5777d 100644
> --- a/libmm-glib/mm-common-helpers.h
> +++ b/libmm-glib/mm-common-helpers.h
> @@ -143,6 +143,8 @@
> gboolean mm_get_int_from_match_info (GMatchInfo
> *match_info,
> gint *out);
> gboolean mm_get_uint_from_str (const gchar *str,
> guint *out);
> +gboolean mm_get_uint_from_hex_str (const gchar *str,
> + guint *out);
> gboolean mm_get_uint_from_match_info (GMatchInfo
> *match_info,
> guint32
> match_index,
> guint *out);
> diff --git a/libmm-glib/mm-kernel-event-properties.c b/libmm-glib/mm-
> kernel-event-properties.c
> new file mode 100644
> index 0000000..5ff52f9
> --- /dev/null
> +++ b/libmm-glib/mm-kernel-event-properties.c
> @@ -0,0 +1,689 @@
> +/* -*- 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) 2016 Velocloud, Inc.
> + */
> +
> +#include <string.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +
> +#include "mm-errors-types.h"
> +#include "mm-enums-types.h"
> +#include "mm-common-helpers.h"
> +#include "mm-kernel-event-properties.h"
> +
> +/**
> + * SECTION: mm-kernel-event-properties
> + * @title: MMKernelEventProperties
> + * @short_description: Helper object to handle kernel event
> properties.
> + *
> + * The #MMKernelEventProperties is an object handling the properties
> to be set
> + * in reported kernel events.
> + *
> + * This object is created by the user and passed to ModemManager
> with either
> + * mm_manager_report_kernel_event() or
> mm_manager_report_kernel_event_sync().
> + */
> +
> +G_DEFINE_TYPE (MMKernelEventProperties, mm_kernel_event_properties,
> G_TYPE_OBJECT)
> +
> +#define PROPERTY_ACTION "action"
> +#define PROPERTY_SUBSYSTEM "subsystem"
> +#define PROPERTY_NAME "name"
> +#define PROPERTY_DRIVER "driver"
> +#define PROPERTY_PHYSDEV_UID "physdev-uid"
> +#define PROPERTY_PHYSDEV_VID "physdev-vid"
> +#define PROPERTY_PHYSDEV_PID "physdev-pid"
> +#define PROPERTY_CANDIDATE "candidate"
> +
> +struct _MMKernelEventPropertiesPrivate {
> + gchar *action;
> + gchar *subsystem;
> + gchar *name;
> + gchar *driver;
> + gchar *physdev_uid;
> + guint16 physdev_vid;
> + guint16 physdev_pid;
> + gboolean candidate;
> +};
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_action:
> + * @self: A #MMKernelEventProperties.
> + * @action: The action to set.
> + *
> + * Sets the action.
> + */
> +void
> +mm_kernel_event_properties_set_action (MMKernelEventProperties
> *self,
> + const
> gchar *action)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + g_free (self->priv->action);
> + self->priv->action = g_strdup (action);
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_action:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the action.
> + *
> + * Returns: (transfer none): The action. Do not free the returned
> value, it is owned by @self.
> + */
> +const gchar *
> +mm_kernel_event_properties_get_action (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> NULL);
> +
> + return self->priv->action;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_subsystem:
> + * @self: A #MMKernelEventProperties.
> + * @subsystem: The subsystem to set.
> + *
> + * Sets the subsystem.
> + */
> +void
> +mm_kernel_event_properties_set_subsystem (MMKernelEventProperties
> *self,
> + const
> gchar *subsystem)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + g_free (self->priv->subsystem);
> + self->priv->subsystem = g_strdup (subsystem);
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_subsystem:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the subsystem.
> + *
> + * Returns: (transfer none): The subsystem. Do not free the returned
> value, it is owned by @self.
> + */
> +const gchar *
> +mm_kernel_event_properties_get_subsystem (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> NULL);
> +
> + return self->priv->subsystem;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_name:
> + * @self: A #MMKernelEventProperties.
> + * @name: The name to set.
> + *
> + * Sets the name.
> + */
> +void
> +mm_kernel_event_properties_set_name (MMKernelEventProperties *self,
> + const gchar *name)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + g_free (self->priv->name);
> + self->priv->name = g_strdup (name);
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_name:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the name.
> + *
> + * Returns: (transfer none): The name. Do not free the returned
> value, it is owned by @self.
> + */
> +const gchar *
> +mm_kernel_event_properties_get_name (MMKernelEventProperties *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> NULL);
> +
> + return self->priv->name;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_driver:
> + * @self: A #MMKernelEventProperties.
> + * @driver: The driver to set.
> + *
> + * Sets the driver.
> + */
> +void
> +mm_kernel_event_properties_set_driver (MMKernelEventProperties
> *self,
> + const
> gchar *driver)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + g_free (self->priv->driver);
> + self->priv->driver = g_strdup (driver);
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_driver:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the driver.
> + *
> + * Returns: (transfer none): The driver. Do not free the returned
> value, it is owned by @self.
> + */
> +const gchar *
> +mm_kernel_event_properties_get_driver (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> NULL);
> +
> + return self->priv->driver;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_physdev_uid:
> + * @self: A #MMKernelEventProperties.
> + * @physdev_uid: The physdev uid to set.
> + *
> + * Sets the unique ID of the physical device.
> + */
> +void
> +mm_kernel_event_properties_set_physdev_uid (MMKernelEventProperties
> *self,
> + const
> gchar *physdev_uid)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + g_free (self->priv->physdev_uid);
> + self->priv->physdev_uid = g_strdup (physdev_uid);
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_physdev_uid:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the unique ID of the physical device.
> + *
> + * Returns: (transfer none): The physdev uid. Do not free the
> returned value, it is owned by @self.
> + */
> +const gchar *
> +mm_kernel_event_properties_get_physdev_uid (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> NULL);
> +
> + return self->priv->physdev_uid;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_physdev_vid:
> + * @self: A #MMKernelEventProperties.
> + * @physdev_vid: The vendor id to set.
> + *
> + * Sets the USB vendor id of the physical device.
> + */
> +void
> +mm_kernel_event_properties_set_physdev_vid (MMKernelEventProperties
> *self,
> + guint16
> physdev_vid)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + self->priv->physdev_vid = physdev_vid;
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_physdev_vid:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the USB vendor id of the physical device.
> + *
> + * Returns: The vendor id.
> + */
> +guint16
> +mm_kernel_event_properties_get_physdev_vid (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), 0);
> +
> + return self->priv->physdev_vid;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_physdev_pid:
> + * @self: A #MMKernelEventProperties.
> + * @physdev_pid: The product id to set.
> + *
> + * Sets the USB product id of the physical device.
> + */
> +void
> +mm_kernel_event_properties_set_physdev_pid (MMKernelEventProperties
> *self,
> + guint16
> physdev_pid)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + self->priv->physdev_pid = physdev_pid;
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_physdev_pid:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the USB product id of the physical device.
> + *
> + * Returns: The product id.
> + */
> +guint16
> +mm_kernel_event_properties_get_physdev_pid (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), 0);
> +
> + return self->priv->physdev_pid;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_set_candidate:
> + * @self: A #MMKernelEventProperties.
> + * @candidate: %TRUE if candidate, %FALSE otherwise.
> + *
> + * Sets the flag specifying wheter the device should be treated as a
> candidate
> + * regardless of any internal rules to filter it out.
> + */
> +void
> +mm_kernel_event_properties_set_candidate (MMKernelEventProperties
> *self,
> + gboolean c
> andidate)
> +{
> + g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
> +
> + self->priv->candidate = candidate;
> +}
> +
> +/**
> + * mm_kernel_event_properties_get_candidate:
> + * @self: A #MMKernelEventProperties.
> + *
> + * Gets the flag specifying wheter the device should be treated as a
> candidate
> + * regardless of any internal rules to filter it out.
> + *
> + * Returns: %TRUE if candidate, %FALSE otherwise.
> + */
> +gboolean
> +mm_kernel_event_properties_get_candidate (MMKernelEventProperties
> *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> FALSE);
> +
> + return self->priv->candidate;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +GVariant *
> +mm_kernel_event_properties_get_dictionary (MMKernelEventProperties
> *self)
> +{
> + GVariantBuilder builder;
> +
> + /* We do allow NULL */
> + if (!self)
> + return NULL;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self),
> NULL);
> +
> + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
> +
> + if (self->priv->action)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_ACTION,
> + g_variant_new_string (self->priv-
> >action));
> +
> + if (self->priv->subsystem)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_SUBSYSTEM,
> + g_variant_new_string (self->priv-
> >subsystem));
> +
> + if (self->priv->name)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_NAME,
> + g_variant_new_string (self->priv-
> >name));
> +
> + if (self->priv->driver)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_DRIVER,
> + g_variant_new_string (self->priv-
> >driver));
> +
> + if (self->priv->physdev_uid)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_PHYSDEV_UID,
> + g_variant_new_string (self->priv-
> >physdev_uid));
> +
> + if (self->priv->physdev_vid)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_PHYSDEV_VID,
> + g_variant_new_uint16 (self->priv-
> >physdev_vid));
> +
> + if (self->priv->physdev_pid)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_PHYSDEV_PID,
> + g_variant_new_uint16 (self->priv-
> >physdev_pid));
> +
> + /* Just avoid adding it if FALSE */
> + if (self->priv->candidate)
> + g_variant_builder_add (&builder,
> + "{sv}",
> + PROPERTY_CANDIDATE,
> + g_variant_new_boolean (TRUE));
> +
> + return g_variant_ref_sink (g_variant_builder_end (&builder));
> +}
> +
> +/*******************************************************************
> **********/
> +
> +static guint16
> +parse_uint16 (const gchar *str,
> + GError **error)
> +{
> + guint num;
> +
> + if (!mm_get_uint_from_hex_str (str, &num) || num > G_MAXUINT16)
> {
> + g_set_error (error,
> + MM_CORE_ERROR,
> + MM_CORE_ERROR_INVALID_ARGS,
> + "Invalid properties string, cannot parse '%s'
> as hexadecimal uint16",
> + str);
> + return 0;
> + }
> +
> + return num;
> +}
> +
> +static gboolean
> +consume_string (MMKernelEventProperties *self,
> + const gchar *key,
> + const gchar *value,
> + GError **error)
> +{
> + if (g_str_equal (key, PROPERTY_ACTION))
> + mm_kernel_event_properties_set_action (self, value);
> + else if (g_str_equal (key, PROPERTY_SUBSYSTEM))
> + mm_kernel_event_properties_set_subsystem (self, value);
> + else if (g_str_equal (key, PROPERTY_NAME))
> + mm_kernel_event_properties_set_name (self, value);
> + else if (g_str_equal (key, PROPERTY_DRIVER))
> + mm_kernel_event_properties_set_driver (self, value);
> + else if (g_str_equal (key, PROPERTY_PHYSDEV_UID))
> + mm_kernel_event_properties_set_physdev_uid (self, value);
> + else if (g_str_equal (key, PROPERTY_PHYSDEV_VID)) {
> + guint16 val;
> + GError *inner_error = NULL;
> +
> + val = parse_uint16 (value, &inner_error);
> + if (inner_error) {
> + g_propagate_error (error, inner_error);
> + return FALSE;
> + }
> + mm_kernel_event_properties_set_physdev_vid (self, val);
> + } else if (g_str_equal (key, PROPERTY_PHYSDEV_PID)) {
> + guint16 val;
> + GError *inner_error = NULL;
> +
> + val = parse_uint16 (value, &inner_error);
> + if (inner_error) {
> + g_propagate_error (error, inner_error);
> + return FALSE;
> + }
> + mm_kernel_event_properties_set_physdev_pid (self, val);
> + } else if (g_str_equal (key, PROPERTY_CANDIDATE)) {
> + gboolean val ;
> + GError *inner_error = NULL;
> +
> + val = mm_common_get_boolean_from_string (value,
> &inner_error);
> + if (inner_error) {
> + g_propagate_error (error, inner_error);
> + return FALSE;
> + }
> + mm_kernel_event_properties_set_candidate (self, val);
> + } else {
> + g_set_error (error,
> + MM_CORE_ERROR,
> + MM_CORE_ERROR_INVALID_ARGS,
> + "Invalid properties string, unexpected key
> '%s'",
> + key);
> + return FALSE;
> + }
> +
> + return TRUE;
> +}
> +
> +typedef struct {
> + MMKernelEventProperties *properties;
> + GError *error;
> +} ParseKeyValueContext;
> +
> +static gboolean
> +key_value_foreach (const gchar *key,
> + const gchar *value,
> + ParseKeyValueContext *ctx)
> +{
> + return consume_string (ctx->properties,
> + key,
> + value,
> + &ctx->error);
> +}
> +
> +MMKernelEventProperties *
> +mm_kernel_event_properties_new_from_string (const gchar *str,
> + GError **error)
> +{
> + ParseKeyValueContext ctx;
> +
> + ctx.properties = mm_kernel_event_properties_new ();
> + ctx.error = NULL;
> +
> + mm_common_parse_key_value_string (str,
> + &ctx.error,
> + (MMParseKeyValueForeachFn)
> key_value_foreach,
> + &ctx);
> +
> + /* If error, destroy the object */
> + if (ctx.error) {
> + g_propagate_error (error, ctx.error);
> + g_object_unref (ctx.properties);
> + ctx.properties = NULL;
> + }
> +
> + return ctx.properties;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +static gboolean
> +consume_variant (MMKernelEventProperties *properties,
> + const gchar *key,
> + GVariant *value,
> + GError **error)
> +{
> + if (g_str_equal (key, PROPERTY_ACTION))
> + mm_kernel_event_properties_set_action (
> + properties,
> + g_variant_get_string (value, NULL));
> + else if (g_str_equal (key, PROPERTY_SUBSYSTEM))
> + mm_kernel_event_properties_set_subsystem (
> + properties,
> + g_variant_get_string (value, NULL));
> + else if (g_str_equal (key, PROPERTY_NAME))
> + mm_kernel_event_properties_set_name (
> + properties,
> + g_variant_get_string (value, NULL));
> + else if (g_str_equal (key, PROPERTY_DRIVER))
> + mm_kernel_event_properties_set_driver (
> + properties,
> + g_variant_get_string (value, NULL));
> + else if (g_str_equal (key, PROPERTY_PHYSDEV_UID))
> + mm_kernel_event_properties_set_physdev_uid (
> + properties,
> + g_variant_get_string (value, NULL));
> + else if (g_str_equal (key, PROPERTY_PHYSDEV_VID))
> + mm_kernel_event_properties_set_physdev_vid (
> + properties,
> + g_variant_get_uint16 (value));
> + else if (g_str_equal (key, PROPERTY_PHYSDEV_PID))
> + mm_kernel_event_properties_set_physdev_pid (
> + properties,
> + g_variant_get_uint16 (value));
> + else {
> + /* Set error */
> + g_set_error (error,
> + MM_CORE_ERROR,
> + MM_CORE_ERROR_INVALID_ARGS,
> + "Invalid properties dictionary, unexpected key
> '%s'",
> + key);
> + return FALSE;
> + }
> +
> + return TRUE;
> +}
> +
> +MMKernelEventProperties *
> +mm_kernel_event_properties_new_from_dictionary
> (GVariant *dictionary,
> + GError **error)
> +{
> + GError *inner_error = NULL;
> + GVariantIter iter;
> + gchar *key;
> + GVariant *value;
> + MMKernelEventProperties *properties;
> +
> + properties = mm_kernel_event_properties_new ();
> + if (!dictionary)
> + return properties;
> +
> + if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE
> ("a{sv}"))) {
> + g_set_error (error,
> + MM_CORE_ERROR,
> + MM_CORE_ERROR_INVALID_ARGS,
> + "Cannot create kernel event properties from
> dictionary: "
> + "invalid variant type received");
> + g_object_unref (properties);
> + return NULL;
> + }
> +
> + g_variant_iter_init (&iter, dictionary);
> + while (!inner_error &&
> + g_variant_iter_next (&iter, "{sv}", &key, &value)) {
> + consume_variant (properties,
> + key,
> + value,
> + &inner_error);
> + g_free (key);
> + g_variant_unref (value);
> + }
> +
> + /* If error, destroy the object */
> + if (inner_error) {
> + g_propagate_error (error, inner_error);
> + g_object_unref (properties);
> + properties = NULL;
> + }
> +
> + return properties;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_dup:
> + * @orig: a #MMKernelEventProperties
> + *
> + * Returns a copy of @orig.
> + *
> + * Returns: (transfer full): a #MMKernelEventProperties
> + */
> +MMKernelEventProperties *
> +mm_kernel_event_properties_dup (MMKernelEventProperties *orig)
> +{
> + GVariant *dict;
> + MMKernelEventProperties *copy;
> + GError *error = NULL;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (orig),
> NULL);
> +
> + dict = mm_kernel_event_properties_get_dictionary (orig);
> + copy = mm_kernel_event_properties_new_from_dictionary (dict,
> &error);
> + g_assert_no_error (error);
> + g_variant_unref (dict);
> +
> + return copy;
> +}
> +
> +/*******************************************************************
> **********/
> +
> +/**
> + * mm_kernel_event_properties_new:
> + *
> + * Creates a new empty #MMKernelEventProperties.
> + *
> + * Returns: (transfer full): a #MMKernelEventProperties. The
> returned value should be freed with g_object_unref().
> + */
> +MMKernelEventProperties *
> +mm_kernel_event_properties_new (void)
> +{
> + return (MM_KERNEL_EVENT_PROPERTIES (g_object_new
> (MM_TYPE_KERNEL_EVENT_PROPERTIES, NULL)));
> +}
> +
> +static void
> +mm_kernel_event_properties_init (MMKernelEventProperties *self)
> +{
> + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
> + MM_TYPE_KERNEL_EVENT_P
> ROPERTIES,
> + MMKernelEventPropertie
> sPrivate);
> +}
> +
> +static void
> +finalize (GObject *object)
> +{
> + MMKernelEventProperties *self = MM_KERNEL_EVENT_PROPERTIES
> (object);
> +
> + g_free (self->priv->action);
> + g_free (self->priv->subsystem);
> + g_free (self->priv->name);
> + g_free (self->priv->driver);
> + g_free (self->priv->physdev_uid);
> +
> + G_OBJECT_CLASS (mm_kernel_event_properties_parent_class)-
> >finalize (object);
> +}
> +
> +static void
> +mm_kernel_event_properties_class_init (MMKernelEventPropertiesClass
> *klass)
> +{
> + GObjectClass *object_class = G_OBJECT_CLASS (klass);
> +
> + g_type_class_add_private (object_class, sizeof
> (MMKernelEventPropertiesPrivate));
> +
> + object_class->finalize = finalize;
> +}
> diff --git a/libmm-glib/mm-kernel-event-properties.h b/libmm-glib/mm-
> kernel-event-properties.h
> new file mode 100644
> index 0000000..eb52b21
> --- /dev/null
> +++ b/libmm-glib/mm-kernel-event-properties.h
> @@ -0,0 +1,113 @@
> +/* -*- 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) 2016 Velocloud, Inc.
> + */
> +
> +#ifndef MM_KERNEL_EVENT_PROPERTIES_H
> +#define MM_KERNEL_EVENT_PROPERTIES_H
> +
> +#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined
> (LIBMM_GLIB_COMPILATION)
> +#error "Only <libmm-glib.h> can be included directly."
> +#endif
> +
> +#include <ModemManager.h>
> +#include <glib-object.h>
> +
> +G_BEGIN_DECLS
> +
> +#define
> MM_TYPE_KERNEL_EVENT_PROPERTIES (mm_kernel_event_propertie
> s_get_type ())
> +#define
> MM_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAS
> T ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventProperties))
> +#define
> MM_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST
> ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES,
> MMKernelEventPropertiesClass))
> +#define
> MM_IS_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYP
> E ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES))
> +#define MM_IS_KERNEL_EVENT_PROPERTIES_CLASS(klass)
> (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES))
> +#define
> MM_KERNEL_EVENT_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS
> ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES,
> MMKernelEventPropertiesClass))
> +
> +typedef struct _MMKernelEventProperties MMKernelEventProperties;
> +typedef struct _MMKernelEventPropertiesClass
> MMKernelEventPropertiesClass;
> +typedef struct _MMKernelEventPropertiesPrivate
> MMKernelEventPropertiesPrivate;
> +
> +/**
> + * MMKernelEventProperties:
> + *
> + * The #MMKernelEventProperties structure contains private data and
> should only be
> + * accessed using the provided API.
> + */
> +struct _MMKernelEventProperties {
> + /*< private >*/
> + GObject parent;
> + MMKernelEventPropertiesPrivate *priv;
> +};
> +
> +struct _MMKernelEventPropertiesClass {
> + /*< private >*/
> + GObjectClass parent;
> +};
> +
> +GType mm_kernel_event_properties_get_type (void);
> +
> +MMKernelEventProperties *mm_kernel_event_properties_new (void);
> +
> +void mm_kernel_event_properties_set_action (MMKernelEv
> entProperties *self,
> + const
> gchar *action);
> +const
> gchar *mm_kernel_event_properties_get_action (MMKernelEventProp
> erties *self);
> +
> +void mm_kernel_event_properties_set_subsystem (MMKernelEv
> entProperties *self,
> + const
> gchar *subsystem);
> +const
> gchar *mm_kernel_event_properties_get_subsystem (MMKernelEventProp
> erties *self);
> +
> +void mm_kernel_event_properties_set_name (MMKernelEv
> entProperties *self,
> + const
> gchar *name);
> +const
> gchar *mm_kernel_event_properties_get_name (MMKernelEventProp
> erties *self);
> +
> +void mm_kernel_event_properties_set_driver (MMKernelEv
> entProperties *self,
> + const
> gchar *driver);
> +const
> gchar *mm_kernel_event_properties_get_driver (MMKernelEventProp
> erties *self);
> +
> +void mm_kernel_event_properties_set_physdev_uid
> (MMKernelEventProperties *self,
> + const
> gchar *physdev_uid);
> +const gchar *mm_kernel_event_properties_get_physdev_uid
> (MMKernelEventProperties *self);
> +
> +void mm_kernel_event_properties_set_physdev_vid
> (MMKernelEventProperties *self,
> + guint16
> physdev_vid);
> +guint16 mm_kernel_event_properties_get_physdev_vid
> (MMKernelEventProperties *self);
> +
> +void mm_kernel_event_properties_set_physdev_pid
> (MMKernelEventProperties *self,
> + guint16
> physdev_pid);
> +guint16 mm_kernel_event_properties_get_physdev_pid
> (MMKernelEventProperties *self);
> +
> +void mm_kernel_event_properties_set_candidate (MMKernelEv
> entProperties *self,
> + gboolean
> candidate);
> +gboolean mm_kernel_event_properties_get_candidate (MMKernelEv
> entProperties *self);
> +
> +/*******************************************************************
> **********/
> +/* ModemManager/libmm-glib/mmcli specific methods */
> +
> +#if defined (_LIBMM_INSIDE_MM) || \
> + defined (_LIBMM_INSIDE_MMCLI) || \
> + defined (LIBMM_GLIB_COMPILATION)
> +
> +MMKernelEventProperties
> *mm_kernel_event_properties_new_from_string (const gchar *str,
> +
> GError **error);
> +
> +MMKernelEventProperties
> *mm_kernel_event_properties_new_from_dictionary
> (GVariant *dictionary,
> +
> GError **error);
> +
> +MMKernelEventProperties
> *mm_kernel_event_properties_dup (MMKernelEventPropert
> ies *orig);
> +
> +GVariant *mm_kernel_event_properties_get_dictionary
> (MMKernelEventProperties *self);
> +
> +#endif
> +
> +G_END_DECLS
> +
> +#endif /* MM_KERNEL_EVENT_PROPERTIES_H */
> diff --git a/libmm-glib/mm-manager.c b/libmm-glib/mm-manager.c
> index cb196b3..6a9ce5d 100644
> --- a/libmm-glib/mm-manager.c
> +++ b/libmm-glib/mm-manager.c
> @@ -497,6 +497,130 @@ mm_manager_scan_devices_sync
> (MMManager *manager,
>
> /*******************************************************************
> **********/
>
> +/**
> + * mm_manager_report_kernel_event_finish:
> + * @manager: A #MMManager.
> + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback
> passed to mm_manager_report_kernel_event().
> + * @error: Return location for error or %NULL.
> + *
> + * Finishes an operation started with
> mm_manager_report_kernel_event().
> + *
> + * Returns: %TRUE if the operation succeded, %FALSE if @error is
> set.
> + */
> +gboolean
> +mm_manager_report_kernel_event_finish (MMManager *manager,
> + GAsyncResult *res,
> + GError **error)
> +{
> + return g_task_propagate_boolean (G_TASK (res), error);
> +}
> +
> +static void
> +report_kernel_event_ready (MmGdbusOrgFreedesktopModemManager1
> *manager_iface_proxy,
> + GAsyncResult *res,
> + GTask *task)
> +{
> + GError *error = NULL;
> +
> + if
> (!mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_fi
> nish (
> + manager_iface_proxy,
> + res,
> + &error))
> + g_task_return_error (task, error);
> + else
> + g_task_return_boolean (task, TRUE);
> + g_object_unref (task);
> +}
> +
> +/**
> + * mm_manager_report_kernel_event:
> + * @manager: A #MMManager.
> + * @properties: the properties of the kernel event.
> + * @cancellable: (allow-none): A #GCancellable or %NULL.
> + * @callback: A #GAsyncReadyCallback to call when the request is
> satisfied or %NULL.
> + * @user_data: User data to pass to @callback.
> + *
> + * Asynchronously report kernel event.
> + *
> + * When the operation is finished, @callback will be invoked in the
> + * <link linkend="g-main-context-push-thread-default">thread-default
> main loop</link>
> + * of the thread you are calling this method from. You can then call
> + * mm_manager_report_kernel_event_finish() to get the result of the
> operation.
> + *
> + * See mm_manager_report_kernel_event_sync() for the synchronous,
> blocking version of this method.
> + */
> +void
> +mm_manager_report_kernel_event (MMManager *manager,
> + MMKernelEventProperties *properties
> ,
> + GCancellable *cancellabl
> e,
> + GAsyncReadyCallback callback,
> + gpointer user_data)
> +{
> + GTask *task;
> + GError *inner_error = NULL;
> + GVariant *dictionary;
> +
> + g_return_if_fail (MM_IS_MANAGER (manager));
> +
> + task = g_task_new (manager, cancellable, callback, user_data);
> +
> + if (!ensure_modem_manager1_proxy (manager, &inner_error)) {
> + g_task_return_error (task, inner_error);
> + g_object_unref (task);
> + return;
> + }
> +
> + dictionary = mm_kernel_event_properties_get_dictionary
> (properties);
> + mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event
> (
> + manager->priv->manager_iface_proxy,
> + dictionary,
> + cancellable,
> + (GAsyncReadyCallback)report_kernel_event_ready,
> + task);
> + g_variant_unref (dictionary);
> +}
> +
> +/**
> + * mm_manager_report_kernel_event_sync:
> + * @manager: A #MMManager.
> + * @properties: the properties of the kernel event.
> + * @cancellable: (allow-none): A #GCancellable or %NULL.
> + * @error: Return location for error or %NULL.
> + *
> + * Synchronously report kernel event.
> + *
> + * The calling thread is blocked until a reply is received.
> + *
> + * See mm_manager_report_kernel_event() for the asynchronous version
> of this method.
> + *
> + * Returns: %TRUE if the operation succeded, %FALSE if @error is
> set.
> + */
> +gboolean
> +mm_manager_report_kernel_event_sync
> (MMManager *manager,
> + MMKernelEventProperties *prope
> rties,
> + GCancellable *cance
> llable,
> + GError **error
> )
> +{
> + GVariant *dictionary;
> + gboolean result;
> +
> + g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
> +
> + if (!ensure_modem_manager1_proxy (manager, error))
> + return FALSE;
> +
> + dictionary = mm_kernel_event_properties_get_dictionary
> (properties);
> + result =
> (mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_syn
> c (
> + manager->priv->manager_iface_proxy,
> + dictionary,
> + cancellable,
> + error));
> + g_variant_unref (dictionary);
> + return result;
> +}
> +
> +/*******************************************************************
> **********/
> +
> static void
> register_dbus_errors (void)
> {
> diff --git a/libmm-glib/mm-manager.h b/libmm-glib/mm-manager.h
> index 96c3066..57a129f 100644
> --- a/libmm-glib/mm-manager.h
> +++ b/libmm-glib/mm-manager.h
> @@ -33,6 +33,7 @@
> #include <ModemManager.h>
>
> #include "mm-gdbus-modem.h"
> +#include "mm-kernel-event-properties.h"
>
> G_BEGIN_DECLS
>
> @@ -108,6 +109,19 @@ gboolean mm_manager_scan_devices_sync
> (MMManager *manager,
> GCancellable *cancellable,
> GError **error);
>
> +void mm_manager_report_kernel_event (MMManager
> *manager,
> + MMKernelEventPropert
> ies *properties,
> + GCancellable
> *cancellable,
> + GAsyncReadyCallback
> callback,
> + gpointer
> user_data);
> +gboolean mm_manager_report_kernel_event_finish
> (MMManager *manager,
> + GAsyncResult
> *res,
> + GError
> **error);
> +gboolean
> mm_manager_report_kernel_event_sync (MMManager *mana
> ger,
> + MMKernelEventPropert
> ies *properties,
> + GCancellable
> *cancellable,
> + GError
> **error);
> +
> G_END_DECLS
>
> #endif /* _MM_MANAGER_H_ */
> diff --git a/src/Makefile.am b/src/Makefile.am
> index f64f1c1..e16d059 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -172,9 +172,15 @@ CLEANFILES += $(PORT_ENUMS_GENERATED)
>
> noinst_LTLIBRARIES += libkerneldevice.la
>
> +libkerneldevice_la_CPPFLAGS = \
> + -DUDEVRULESDIR=\"$(udevrulesdir)\" \
> + $(NULL)
> +
> libkerneldevice_la_SOURCES = \
> kerneldevice/mm-kernel-device.h \
> kerneldevice/mm-kernel-device.c \
> + kerneldevice/mm-kernel-device-generic.h \
> + kerneldevice/mm-kernel-device-generic.c \
> $(NULL)
>
> if WITH_UDEV
> diff --git a/src/kerneldevice/mm-kernel-device-generic.c
> b/src/kerneldevice/mm-kernel-device-generic.c
> new file mode 100644
> index 0000000..04fce70
> --- /dev/null
> +++ b/src/kerneldevice/mm-kernel-device-generic.c
> @@ -0,0 +1,1010 @@
> +/* -*- 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) 2016 Velocloud, Inc.
> + */
> +
> +#define _GNU_SOURCE
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#define _LIBMM_INSIDE_MM
> +#include <libmm-glib.h>
> +
> +#include "mm-kernel-device-generic.h"
> +#include "mm-log.h"
> +
> +#if !defined UDEVRULESDIR
> +# error UDEVRULESDIR is not defined
> +#endif
> +
> +/* Define the following symbol to enable verbose rule parsing traces
> */
> +#undef TRACE_UDEV_RULES
> +
> +#if defined TRACE_UDEV_RULES
> +# define trace(...) mm_dbg (__VA_ARGS__)
> +#else
> +# define trace(...)
> +#endif
> +
> +static void initable_iface_init (GInitableIface *iface);
> +
> +G_DEFINE_TYPE_EXTENDED (MMKernelDeviceGeneric,
> mm_kernel_device_generic, MM_TYPE_KERNEL_DEVICE, 0,
> + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
> initable_iface_init))
> +
> +enum {
> + PROP_0,
> + PROP_PROPERTIES,
> + PROP_LAST
> +};
> +
> +static GParamSpec *properties[PROP_LAST];
> +
> +struct _MMKernelDeviceGenericPrivate {
> + /* Input properties */
> + MMKernelEventProperties *properties;
> +
> + /* Contents from sysfs */
> + gchar *driver;
> + gchar *sysfs_path;
> + gchar *parent_sysfs_path;
> + gchar *physdev_sysfs_path;
> + guint16 physdev_vid;
> + guint16 physdev_pid;
> +};
> +
> +static guint
> +read_sysfs_property_as_hex (const gchar *path,
> + const gchar *property)
> +{
> + gchar *aux;
> + gchar *contents = NULL;
> + guint val = 0;
> +
> + aux = g_strdup_printf ("%s/%s", path, property);
> + if (g_file_get_contents (aux, &contents, NULL, NULL)) {
> + g_strdelimit (contents, "\r\n", ' ');
> + g_strstrip (contents);
> + mm_get_uint_from_hex_str (contents, &val);
> + }
> + g_free (contents);
> + g_free (aux);
> + return val;
> +}
> +
> +static gchar *
> +read_sysfs_property_as_string (const gchar *path,
> + const gchar *property)
> +{
> + gchar *aux;
> + gchar *contents = NULL;
> +
> + aux = g_strdup_printf ("%s/%s", path, property);
> + if (g_file_get_contents (aux, &contents, NULL, NULL)) {
> + g_strdelimit (contents, "\r\n", ' ');
> + g_strstrip (contents);
> + }
> + g_free (aux);
> + return contents;
> +}
> +
> +/*******************************************************************
> **********/
> +/* Load contents */
> +
> +static void
> +preload_sysfs_path (MMKernelDeviceGeneric *self)
> +{
> + gchar *tmp;
> +
> + if (self->priv->sysfs_path)
> + return;
> +
> + /* sysfs can be built directly using subsystem and name; e.g.
> for subsystem
> + * usbmisc and name cdc-wdm0:
> + * $ realpath /sys/class/usbmisc/cdc-wdm0
> + * /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3/4-
> 1.3:1.8/usbmisc/cdc-wdm0
> + */
> + tmp = g_strdup_printf ("/sys/class/%s/%s",
> + mm_kernel_event_properties_get_subsystem
> (self->priv->properties),
> + mm_kernel_event_properties_get_name
> (self->priv->properties));
> +
> + self->priv->sysfs_path = canonicalize_file_name (tmp);
> + if (!self->priv->sysfs_path || !g_file_test (self->priv-
> >sysfs_path, G_FILE_TEST_EXISTS)) {
> + mm_warn ("Invalid sysfs path read for %s/%s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + g_clear_pointer (&self->priv->sysfs_path, g_free);
> + }
> +
> + if (self->priv->sysfs_path)
> + mm_dbg ("(%s/%s) sysfs path: %s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + self->priv->sysfs_path);
> + g_free (tmp);
> +}
> +
> +static void
> +preload_parent_sysfs_path (MMKernelDeviceGeneric *self)
> +{
> + gchar *dirpath;
> + gchar *aux;
> +
> + if (self->priv->parent_sysfs_path || !self->priv->sysfs_path)
> + return;
> +
> + /* parent sysfs can be built directly using subsystem and name;
> e.g. for
> + * subsystem usbmisc and name cdc-wdm0:
> + * $ realpath /sys/class/usbmisc/cdc-wdm0/device
> + * /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3/4-
> 1.3:1.8
> + *
> + * This sysfs path will be equal for all devices exported from
> within the
> + * same interface (e.g. a pair of cdc-wdm/wwan devices).
> + *
> + * The correct parent dir we want to have is the first one with
> "usb" subsystem.
> + */
> + aux = g_strdup_printf ("%s/device", self->priv->sysfs_path);
> + dirpath = canonicalize_file_name (aux);
> + g_free (aux);
> +
> + while (dirpath) {
> + gchar *subsystem_filepath;
> +
> + /* Directory must exist */
> + if (!g_file_test (dirpath, G_FILE_TEST_EXISTS))
> + break;
> +
> + /* If subsystem file not found, keep looping */
> + subsystem_filepath = g_strdup_printf ("%s/subsystem",
> dirpath);
> + if (g_file_test (subsystem_filepath, G_FILE_TEST_EXISTS)) {
> + gchar *canonicalized_subsystem;
> + gchar *subsystem_name;
> +
> + canonicalized_subsystem = canonicalize_file_name
> (subsystem_filepath);
> + g_free (subsystem_filepath);
> +
> + subsystem_name = g_path_get_basename
> (canonicalized_subsystem);
> + g_free (canonicalized_subsystem);
> +
> + if (subsystem_name && g_str_equal (subsystem_name,
> "usb")) {
> + self->priv->parent_sysfs_path = dirpath;
> + g_free (subsystem_name);
> + break;
> + }
> + } else
> + g_free (subsystem_filepath);
> +
> + /* Just in case */
> + if (g_str_equal (dirpath, "/")) {
> + g_free (dirpath);
> + break;
> + }
> +
> + aux = g_path_get_dirname (dirpath);
> + g_free (dirpath);
> + dirpath = aux;
> + }
> +
> + if (self->priv->parent_sysfs_path)
> + mm_dbg ("(%s/%s) parent sysfs path: %s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + self->priv->parent_sysfs_path);
> +}
> +
> +static void
> +preload_physdev_sysfs_path (MMKernelDeviceGeneric *self)
> +{
> + /* physdev sysfs is the dirname of the parent sysfs path, e.g.:
> + * /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3
> + *
> + * This sysfs path will be equal for all devices exported from
> the same
> + * physical device.
> + */
> + if (!self->priv->physdev_sysfs_path && self->priv-
> >parent_sysfs_path)
> + self->priv->physdev_sysfs_path = g_path_get_dirname (self-
> >priv->parent_sysfs_path);
> +
> + if (self->priv->physdev_sysfs_path)
> + mm_dbg ("(%s/%s) physdev sysfs path: %s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + self->priv->physdev_sysfs_path);
> +}
> +
> +static void
> +preload_driver (MMKernelDeviceGeneric *self)
> +{
> + if (!self->priv->driver && self->priv->parent_sysfs_path) {
> + gchar *tmp;
> + gchar *tmp2;
> +
> + tmp = g_strdup_printf ("%s/driver", self->priv-
> >parent_sysfs_path);
> + tmp2 = canonicalize_file_name (tmp);
> + if (tmp2 && g_file_test (tmp2, G_FILE_TEST_EXISTS))
> + self->priv->driver = g_path_get_basename (tmp2);
> + g_free (tmp2);
> + g_free (tmp);
> + }
> +
> + if (self->priv->driver)
> + mm_dbg ("(%s/%s) driver: %s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + self->priv->driver);
> +}
> +
> +static void
> +preload_physdev_vid (MMKernelDeviceGeneric *self)
> +{
> + if (!self->priv->physdev_vid && self->priv->physdev_sysfs_path)
> {
> + guint val;
> +
> + val = read_sysfs_property_as_hex (self->priv-
> >physdev_sysfs_path, "idVendor");
> + if (val && val <= G_MAXUINT16)
> + self->priv->physdev_vid = val;
> + }
> +
> + if (self->priv->physdev_vid)
> + mm_dbg ("(%s/%s) vid: 0x%04x",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + self->priv->physdev_vid);
> +}
> +
> +static void
> +preload_physdev_pid (MMKernelDeviceGeneric *self)
> +{
> + if (!self->priv->physdev_pid && self->priv->physdev_sysfs_path)
> {
> + guint val;
> +
> + val = read_sysfs_property_as_hex (self->priv-
> >physdev_sysfs_path, "idProduct");
> + if (val && val <= G_MAXUINT16)
> + self->priv->physdev_pid = val;
> + }
> +
> + if (self->priv->physdev_pid)
> + mm_dbg ("(%s/%s) pid: 0x%04x",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + self->priv->physdev_pid);
> +}
> +
> +static void
> +preload_contents (MMKernelDeviceGeneric *self)
> +{
> + preload_sysfs_path (self);
> + preload_parent_sysfs_path (self);
> + preload_physdev_sysfs_path (self);
> +
> + if (!mm_kernel_event_properties_get_driver
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties))
> + preload_driver (self);
> +
> + if (!mm_kernel_event_properties_get_physdev_vid
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties))
> + preload_physdev_vid (self);
> +
> + if (!mm_kernel_event_properties_get_physdev_pid
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties))
> + preload_physdev_pid (self);
> +}
> +
> +/*******************************************************************
> **********/
> +
> +static const gchar *
> +kernel_device_get_subsystem (MMKernelDevice *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + return mm_kernel_event_properties_get_subsystem
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
> +}
> +
> +static const gchar *
> +kernel_device_get_name (MMKernelDevice *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + return mm_kernel_event_properties_get_name
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
> +}
> +
> +static const gchar *
> +kernel_device_get_sysfs_path (MMKernelDevice *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path;
> +}
> +
> +static const gchar *
> +kernel_device_get_parent_sysfs_path (MMKernelDevice *self)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + return MM_KERNEL_DEVICE_GENERIC (self)->priv->parent_sysfs_path;
> +}
> +
> +static const gchar *
> +kernel_device_get_physdev_uid (MMKernelDevice *self)
> +{
> + const gchar *physdev_uid;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + /* Prefer the one coming in the properties, if any */
> + physdev_uid = mm_kernel_event_properties_get_physdev_uid
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
> + if (!physdev_uid)
> + physdev_uid = MM_KERNEL_DEVICE_GENERIC (self)->priv-
> >physdev_sysfs_path;
> + /* If there is no physdev sysfs path, e.g. for platform ports,
> use the device sysfs itself */
> + if (!physdev_uid)
> + physdev_uid = MM_KERNEL_DEVICE_GENERIC (self)->priv-
> >sysfs_path;
> + return physdev_uid;
> +}
> +
> +static const gchar *
> +kernel_device_get_driver (MMKernelDevice *self)
> +{
> + const gchar *driver;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + /* Prefer the one coming in the properties, if any */
> + driver = mm_kernel_event_properties_get_driver
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
> + if (!driver)
> + driver = MM_KERNEL_DEVICE_GENERIC (self)->priv->driver;
> + return driver;
> +}
> +
> +static guint16
> +kernel_device_get_physdev_vid (MMKernelDevice *self)
> +{
> + guint16 physdev_vid;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), 0);
> +
> + /* Prefer the one coming in the properties, if any */
> + physdev_vid = mm_kernel_event_properties_get_physdev_vid
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
> + if (!physdev_vid)
> + physdev_vid = MM_KERNEL_DEVICE_GENERIC (self)->priv-
> >physdev_vid;
> +
> + return physdev_vid;
> +}
> +
> +static guint16
> +kernel_device_get_physdev_pid (MMKernelDevice *self)
> +{
> + guint16 physdev_pid;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), 0);
> +
> + /* Prefer the one coming in the properties, if any */
> + physdev_pid = mm_kernel_event_properties_get_physdev_pid
> (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
> + if (!physdev_pid)
> + physdev_pid = MM_KERNEL_DEVICE_GENERIC (self)->priv-
> >physdev_pid;
> +
> + return physdev_pid;
> +}
> +
> +static gboolean
> +kernel_device_is_candidate (MMKernelDevice *_self,
> + gboolean manual_scan)
> +{
> + MMKernelDeviceGeneric *self;
> + const gchar *name;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (_self),
> FALSE);
> +
> + self = MM_KERNEL_DEVICE_GENERIC (_self);
> +
> + /* If being candidate is forced, do it */
> + if (mm_kernel_event_properties_get_candidate (self->priv-
> >properties)) {
> + mm_dbg ("(%s/%s) device forced to be candidate",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + return TRUE;
> + }
> +
> + name = mm_kernel_event_properties_get_name (self->priv-
> >properties);
> +
> + /* ignore VTs */
> + if (strncmp (name, "tty", 3) == 0 && g_ascii_isdigit (name[3]))
> {
> + mm_dbg ("(%s/%s) VT ignored",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + return FALSE;
> + }
> +
> + /* only ports tagged as candidate */
> + if (!mm_kernel_device_get_property_as_boolean (_self,
> "ID_MM_CANDIDATE")) {
> + mm_dbg ("(%s/%s) device not flagged with ID_MM_CANDIDATE",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + return FALSE;
> + }
> +
> + /* no devices without physical device */
> + if (!self->priv->physdev_sysfs_path) {
> + mm_dbg ("(%s/%s) device without physdev sysfs path",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + return FALSE;
> + }
> +
> + /* ignore ports explicitly ignored; note that in this case the
> property
> + * is set in this kernel device itself, unlike in the udev
> backend, that
> + * goes in the parent udev device */
> + if (mm_kernel_device_get_property_as_boolean (_self,
> "ID_MM_DEVICE_IGNORE")) {
> + mm_dbg ("(%s/%s) device flagged with ID_MM_DEVICE_IGNORE",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + return FALSE;
> + }
> +
> + mm_dbg ("(%s/%s) device is candidate",
> + mm_kernel_event_properties_get_subsystem (self->priv-
> >properties),
> + mm_kernel_event_properties_get_name (self->priv-
> >properties));
> + return TRUE;
> +}
> +
> +static gboolean
> +kernel_device_cmp (MMKernelDevice *a,
> + MMKernelDevice *b)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (a), FALSE);
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (b), FALSE);
> +
> + return (!g_strcmp0 (mm_kernel_device_get_subsystem (a),
> mm_kernel_device_get_subsystem (b)) &&
> + !g_strcmp0 (mm_kernel_device_get_name (a),
> mm_kernel_device_get_name (b)));
> +}
> +
> +/*******************************************************************
> **********/
> +
> +static const gchar *expected_rules_prefix[] = { "77-mm-", "80-mm-"
> };
> +
> +static GList *
> +list_rule_files (void)
> +{
> + GFile *udevrulesdir;
> + GFileEnumerator *enumerator;
> + GList *children = NULL;
> +
> + udevrulesdir = g_file_new_for_path (UDEVRULESDIR);
> + enumerator = g_file_enumerate_children (udevrulesdir,
> + G_FILE_ATTRIBUTE_STAND
> ARD_NAME,
> + G_FILE_QUERY_INFO_NONE
> ,
> + NULL,
> + NULL);
> + if (enumerator) {
> + GFileInfo *info;
> +
> + /* If we get any kind of error, assume we need to stop
> enumerating */
> + while ((info = g_file_enumerator_next_file (enumerator,
> NULL, NULL)) != NULL) {
> + guint i;
> +
> + for (i = 0; i < G_N_ELEMENTS (expected_rules_prefix);
> i++) {
> + if (g_str_has_prefix (g_file_info_get_name (info),
> expected_rules_prefix[i])) {
> + children = g_list_prepend (children,
> g_build_path (G_DIR_SEPARATOR_S, UDEVRULESDIR, g_file_info_get_name
> (info), NULL));
> + break;
> + }
> + }
> + g_object_unref (info);
> + }
> + g_object_unref (enumerator);
> + }
> + g_object_unref (udevrulesdir);
> +
> + return g_list_sort (children, (GCompareFunc) g_strcmp0);
> +}
> +
> +static gboolean
> +apply_result (MMKernelDeviceGeneric *self,
> + gchar *rule,
> + gchar **goto_label)
> +{
> + gchar *aux;
> + gchar *right;
> +
> + g_strstrip (rule);
> +
> + aux = strchr (rule, '=');
> + if (!aux) {
> + mm_warn ("Invalid rule to apply: missing '='");
> + return FALSE;
> + }
> + right = aux + 1;
> + *aux = '\0';
> +
> + if (strlen (right) == 0) {
> + mm_warn ("Invalid rule to apply: missing right operand");
> + return FALSE;
> + }
> +
> + if (g_str_equal (rule, "GOTO")) {
> + *goto_label = g_strdup (right);
> + return TRUE;
> + }
> +
> + /* Just ignore */
> + if (g_str_equal (rule, "LABEL"))
> + return TRUE;
> +
> + if (g_str_has_prefix (rule, "ENV")) {
> + rule = &rule[3];
> + g_strdelimit (rule, "{}", ' ');
> + g_strstrip (rule);
> + if (strlen (rule) == 0) {
> + mm_warn ("Invalid rule to apply: empty property name");
> + return FALSE;
> + }
> +
> + g_strdelimit (right, "\"'", ' ');
> + g_strstrip (right);
> + if (strlen (right) == 0) {
> + mm_warn ("Invalid rule to apply: empty property value");
> + return FALSE;
> + }
> +
> + mm_dbg ("(%s/%s) rule applied: %s=%s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + rule, right);
> + g_object_set_data_full (G_OBJECT (self), rule, g_strdup
> (right), (GDestroyNotify) g_free);
> + return TRUE;
> + }
> +
> + mm_warn ("Invalid rule to apply: unknown action '%s'", rule);
> + return FALSE;
> +}
> +
> +static gboolean
> +string_match (const gchar *str,
> + const gchar *original_pattern)
> +{
> + gchar *pattern;
> + gchar *start;
> + gboolean open_prefix = FALSE;
> + gboolean open_suffix = FALSE;
> + gboolean match;
> +
> + pattern = g_strdup (original_pattern);
> + start = pattern;
> +
> + if (start[0] == '*') {
> + open_prefix = TRUE;
> + start++;
> + }
> +
> + if (start[strlen (start) - 1] == '*') {
> + open_suffix = TRUE;
> + start[strlen (start) - 1] = '\0';
> + }
> +
> + if (open_suffix && !open_prefix)
> + match = g_str_has_prefix (str, start);
> + else if (!open_suffix && open_prefix)
> + match = g_str_has_suffix (str, start);
> + else if (open_suffix && open_prefix)
> + match = !!strstr (str, start);
> + else
> + match = g_str_equal (str, start);
> +
> + g_free (pattern);
> + return match;
> +}
> +
> +static gboolean
> +validate_condition (MMKernelDeviceGeneric *self,
> + gchar *condition)
> +{
> + gboolean condition_equal;
> + gchar *aux;
> + gchar *right;
> + guint val;
> +
> + g_strstrip (condition);
> +
> + if ((aux = strstr (condition, "==")) != NULL)
> + condition_equal = TRUE;
> + else if ((aux = strstr (condition, "!=")) != NULL)
> + condition_equal = FALSE;
> + else {
> + mm_warn ("Invalid rule to apply: unknown condition: %s",
> condition);
> + return FALSE;
> + }
> +
> + *aux = '\0';
> + right = &aux[2];
> + g_strdelimit (right, "\"'", ' ');
> + g_strstrip (right);
> +
> + trace ("condition: '%s', operation: '%s', right: '%s'",
> + condition, condition_equal ? "==" : "!=", right);
> +
> + /* We only apply 'add' rules */
> + if (g_str_equal (condition, "ACTION"))
> + return ((!!strstr (right, "add")) == condition_equal);
> +
> + /* We look for the subsystem string in the whole sysfs path.
> + *
> + * Note that we're not really making a difference between
> "SUBSYSTEMS"
> + * (where the whole device tree is checked) and "SUBSYSTEM"
> (where just one
> + * single device is checked), because a lot of the MM udev rules
> are meant
> + * to just tag the physical device (e.g. with
> ID_MM_DEVICE_IGNORE) instead
> + * of the single ports. In our case with the custom parsing, we
> do tag all
> + * independent ports.
> + */
> + if (g_str_equal (condition, "SUBSYSTEMS") || g_str_equal
> (condition, "SUBSYSTEM"))
> + return ((self->priv->sysfs_path && !!strstr (self->priv-
> >sysfs_path, right)) == condition_equal);
> +
> + /* Exact DRIVER match? We also include the check for DRIVERS,
> even if we
> + * only apply it to this port driver. */
> + if (g_str_equal (condition, "DRIVER") || g_str_equal (condition,
> "DRIVERS"))
> + return ((!g_strcmp0 (right, mm_kernel_device_get_driver
> (MM_KERNEL_DEVICE (self)))) == condition_equal);
> +
> + /* Device name checks */
> + if (g_str_equal (condition, "KERNEL"))
> + return (string_match (mm_kernel_device_get_name
> (MM_KERNEL_DEVICE (self)), right) == condition_equal);
> +
> + /* Device sysfs path checks */
> + if (g_str_equal (condition, "DEVPATH"))
> + return (string_match (mm_kernel_device_get_sysfs_path
> (MM_KERNEL_DEVICE (self)), right) == condition_equal);
> +
> + /* Attributes checks */
> + if (g_str_has_prefix (condition, "ATTRS")) {
> + gchar *contents;
> + gboolean result;
> +
> + condition = &condition[5];
> + g_strdelimit (condition, "{}", ' ');
> + g_strstrip (condition);
> + g_strdelimit (right, "\"'", ' ');
> + g_strstrip (right);
> +
> + /* VID/PID directly from our API */
> + if (g_str_equal (condition, "idVendor"))
> + return ((mm_kernel_device_get_physdev_vid
> (MM_KERNEL_DEVICE (self)) == mm_get_uint_from_hex_str (right, &val))
> == condition_equal);
> + if (g_str_equal (condition, "idProduct"))
> + return ((mm_kernel_device_get_physdev_pid
> (MM_KERNEL_DEVICE (self)) == mm_get_uint_from_hex_str (right, &val))
> == condition_equal);
> +
> + /* manufacturer/product in the physdev */
> + if (g_str_equal (condition, "manufacturer") || g_str_equal
> (condition, "product")) {
> + contents = (self->priv->physdev_sysfs_path ?
> read_sysfs_property_as_string (self->priv->sysfs_path, condition) :
> NULL);
> + result = ((contents && g_str_equal (contents, right)) ==
> condition_equal);
> + g_free (contents);
> + return result;
> + }
> +
> + /* interface class/subclass/number in the exact device */
> + if (g_str_equal (condition, "bInterfaceClass") ||
> + g_str_equal (condition, "bInterfaceSubClass") ||
> + g_str_equal (condition, "bInterfaceProtocol")) {
> + contents = read_sysfs_property_as_string (self->priv-
> >sysfs_path, condition);
> + result = ((contents && g_str_equal (contents, right)) ==
> condition_equal);
> + g_free (contents);
> + return result;
> + }
> +
> + mm_warn ("Invalid ATTRS: %s", condition);
> + return FALSE;
> + }
> +
> + mm_warn ("Invalid rule to apply: unknown left operand: %s",
> condition);
> + return FALSE;
> +}
> +
> +static gboolean
> +process_rule (MMKernelDeviceGeneric *self,
> + const gchar *line,
> + gchar **goto_label)
> +{
> + gchar **split;
> + guint n_items, i;
> + gboolean applied = FALSE;
> + gboolean validated = TRUE;
> +
> + split = g_strsplit (line, ",", -1);
> + n_items = g_strv_length (split);
> + for (i = 0; validated && i < n_items; i++) {
> + gchar *rule;
> +
> + rule = g_strdup (split[i]);
> + if (i < (n_items - 1)) {
> + /* If one of the condition fails, break the rule */
> + validated = validate_condition (self, rule);
> + trace ("condition %s: %s", split[i], validated ? "valid"
> : "invalid");
> + }
> + else {
> + /* Last item, the rule to apply */
> + applied = apply_result (self, rule, goto_label);
> + trace ("result %s: %s", split[i], applied ? "applied" :
> "not applied");
> + }
> +
> + g_free (rule);
> + }
> + g_strfreev (split);
> + return applied;
> +}
> +
> +static void
> +preload_properties_from_file (MMKernelDeviceGeneric *self,
> + const gchar *path)
> +{
> + GFile *file;
> + GFileInputStream *fistream;
> + GDataInputStream *distream = NULL;
> + GError *error = NULL;
> + gchar *line;
> + gchar *goto_label = NULL;
> +
> + file = g_file_new_for_path (path);
> + fistream = g_file_read (file, NULL, &error);
> + if (!fistream)
> + goto out;
> +
> + distream = g_data_input_stream_new (G_INPUT_STREAM (fistream));
> +
> + while ((line = g_data_input_stream_read_line_utf8 (distream,
> NULL, NULL, &error)) != NULL) {
> + const gchar *aux;
> +
> + if (goto_label) {
> + if (strstr (line, goto_label))
> + g_clear_pointer (&goto_label, g_free);
> + goto next;
> + }
> +
> + aux = line;
> + while (*aux == ' ')
> + aux++;
> + if (*aux == '#' || *aux == '\0')
> + goto next;
> +
> + process_rule (self, line, &goto_label);
> +
> + next:
> + g_free (line);
> + }
> +
> + g_clear_pointer (&goto_label, g_free);
> +
> +out:
> + if (error) {
> + mm_dbg ("(%s/%s) couldn't load properties from: %s: %s",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties),
> + path,
> + error->message);
> + g_error_free (error);
> + }
> +
> + if (distream)
> + g_object_unref (distream);
> + if (fistream)
> + g_object_unref (fistream);
> + g_object_unref (file);
> +}
> +
> +static void
> +preload_properties (MMKernelDeviceGeneric *self)
> +{
> + GList *rules, *l;
> +
> + rules = list_rule_files ();
> + for (l = rules; l; l = g_list_next (l)) {
> + trace ("(%s/%s) loading properties from: %s",
> + mm_kernel_event_properties_get_subsystem (self->priv-
> >properties),
> + mm_kernel_event_properties_get_name (self->priv-
> >properties),
> + (const gchar *)(l->data));
> + preload_properties_from_file (self, (const gchar *)(l-
> >data));
> + }
> + g_list_free_full (rules, (GDestroyNotify) g_free);
> +}
> +
> +static gboolean
> +kernel_device_has_property (MMKernelDevice *self,
> + const gchar *property)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self),
> FALSE);
> +
> + return !!g_object_get_data (G_OBJECT (self), property);
> +}
> +
> +static const gchar *
> +kernel_device_get_property (MMKernelDevice *self,
> + const gchar *property)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), NULL);
> +
> + return g_object_get_data (G_OBJECT (self), property);
> +}
> +
> +static gboolean
> +kernel_device_get_property_as_boolean (MMKernelDevice *self,
> + const gchar *property)
> +{
> + const gchar *value;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self),
> FALSE);
> +
> + value = g_object_get_data (G_OBJECT (self), property);
> + return (value && mm_common_get_boolean_from_string (value,
> NULL));
> +}
> +
> +static gint
> +kernel_device_get_property_as_int (MMKernelDevice *self,
> + const gchar *property)
> +{
> + const gchar *value;
> + gint aux = 0;
> +
> + g_return_val_if_fail (MM_IS_KERNEL_DEVICE_GENERIC (self), -1);
> +
> + value = g_object_get_data (G_OBJECT (self), property);
> + return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0);
> +}
> +
> +/*******************************************************************
> **********/
> +
> +MMKernelDevice *
> +mm_kernel_device_generic_new (MMKernelEventProperties *properties,
> + GError **error)
> +{
> + g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES
> (properties), NULL);
> +
> + return MM_KERNEL_DEVICE (g_initable_new
> (MM_TYPE_KERNEL_DEVICE_GENERIC,
> + NULL,
> + error,
> + "properties",
> properties,
> + NULL));
> +}
> +
> +/*******************************************************************
> **********/
> +
> +static void
> +mm_kernel_device_generic_init (MMKernelDeviceGeneric *self)
> +{
> + /* Initialize private data */
> + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
> MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericPrivate);
> +}
> +
> +static void
> +set_property (GObject *object,
> + guint prop_id,
> + const GValue *value,
> + GParamSpec *pspec)
> +{
> + MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
> +
> + switch (prop_id) {
> + case PROP_PROPERTIES:
> + g_assert (!self->priv->properties);
> + self->priv->properties = g_value_dup_object (value);
> + /* Don't preload on "remove" actions, where we don't have
> the device any more,
> + * or for devices in the 'virtual' subsystem */
> + if (self->priv->properties &&
> + g_strcmp0 (mm_kernel_event_properties_get_action (self-
> >priv->properties), "remove") &&
> + g_strcmp0 (mm_kernel_event_properties_get_subsystem
> (self->priv->properties), "virtual")) {
> + preload_contents (self);
> + preload_properties (self);
> + }
> + 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)
> +{
> + MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
> +
> + switch (prop_id) {
> + case PROP_PROPERTIES:
> + g_value_set_object (value, self->priv->properties);
> + break;
> + default:
> + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
> + break;
> + }
> +}
> +
> +static gboolean
> +initable_init (GInitable *initable,
> + GCancellable *cancellable,
> + GError **error)
> +{
> + MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC
> (initable);
> +
> + if (!mm_kernel_device_get_subsystem (MM_KERNEL_DEVICE (self))) {
> + g_set_error (error, MM_CORE_ERROR,
> MM_CORE_ERROR_INVALID_ARGS,
> + "subsystem is mandatory in kernel device");
> + return FALSE;
> + }
> +
> + if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) {
> + g_set_error (error, MM_CORE_ERROR,
> MM_CORE_ERROR_INVALID_ARGS,
> + "name is mandatory in kernel device");
> + return FALSE;
> + }
> +
> + /* sysfs path is mandatory as output, and will only be given if
> the
> + * specified device exists; but only if this wasn't a 'remove'
> event
> + * and not a virtual device.
> + */
> + if (self->priv->properties &&
> + g_strcmp0 (mm_kernel_event_properties_get_action (self-
> >priv->properties), "remove") &&
> + g_strcmp0 (mm_kernel_event_properties_get_subsystem (self-
> >priv->properties), "virtual") &&
> + !self->priv->sysfs_path) {
> + g_set_error (error, MM_CORE_ERROR,
> MM_CORE_ERROR_INVALID_ARGS,
> + "device %s/%s not found",
> + mm_kernel_event_properties_get_subsystem (self-
> >priv->properties),
> + mm_kernel_event_properties_get_name (self-
> >priv->properties));
> + return FALSE;
> + }
> +
> + return TRUE;
> +}
> +
> +static void
> +dispose (GObject *object)
> +{
> + MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
> +
> + g_clear_pointer (&self->priv->physdev_sysfs_path, g_free);
> + g_clear_pointer (&self->priv->parent_sysfs_path, g_free);
> + g_clear_pointer (&self->priv->sysfs_path, g_free);
> + g_clear_object (&self->priv->properties);
> +
> + G_OBJECT_CLASS (mm_kernel_device_generic_parent_class)->dispose
> (object);
> +}
> +
> +static void
> +initable_iface_init (GInitableIface *iface)
> +{
> + iface->init = initable_init;
> +}
> +
> +static void
> +mm_kernel_device_generic_class_init (MMKernelDeviceGenericClass
> *klass)
> +{
> + GObjectClass *object_class = G_OBJECT_CLASS
> (klass);
> + MMKernelDeviceClass *kernel_device_class =
> MM_KERNEL_DEVICE_CLASS (klass);
> +
> + g_type_class_add_private (object_class, sizeof
> (MMKernelDeviceGenericPrivate));
> +
> + object_class->dispose = dispose;
> + object_class->get_property = get_property;
> + object_class->set_property = set_property;
> +
> + kernel_device_class->get_subsystem =
> kernel_device_get_subsystem;
> + kernel_device_class->get_name =
> kernel_device_get_name;
> + kernel_device_class->get_driver =
> kernel_device_get_driver;
> + kernel_device_class->get_sysfs_path =
> kernel_device_get_sysfs_path;
> + kernel_device_class->get_physdev_uid =
> kernel_device_get_physdev_uid;
> + kernel_device_class->get_physdev_vid =
> kernel_device_get_physdev_vid;
> + kernel_device_class->get_physdev_pid =
> kernel_device_get_physdev_pid;
> + kernel_device_class->get_parent_sysfs_path =
> kernel_device_get_parent_sysfs_path;
> + kernel_device_class->is_candidate =
> kernel_device_is_candidate;
> + kernel_device_class->cmp =
> kernel_device_cmp;
> + kernel_device_class->has_property =
> kernel_device_has_property;
> + kernel_device_class->get_property =
> kernel_device_get_property;
> + kernel_device_class->get_property_as_boolean =
> kernel_device_get_property_as_boolean;
> + kernel_device_class->get_property_as_int =
> kernel_device_get_property_as_int;
> +
> + properties[PROP_PROPERTIES] =
> + g_param_spec_object ("properties",
> + "Properties",
> + "Generic kernel event properties",
> + MM_TYPE_KERNEL_EVENT_PROPERTIES,
> + G_PARAM_READWRITE |
> G_PARAM_CONSTRUCT_ONLY);
> + g_object_class_install_property (object_class, PROP_PROPERTIES,
> properties[PROP_PROPERTIES]);
> +}
> diff --git a/src/kerneldevice/mm-kernel-device-generic.h
> b/src/kerneldevice/mm-kernel-device-generic.h
> new file mode 100644
> index 0000000..87f9994
> --- /dev/null
> +++ b/src/kerneldevice/mm-kernel-device-generic.h
> @@ -0,0 +1,51 @@
> +/* -*- 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) 2016 Velocloud, Inc.
> + */
> +
> +#ifndef MM_KERNEL_DEVICE_GENERIC_H
> +#define MM_KERNEL_DEVICE_GENERIC_H
> +
> +#include <glib.h>
> +#include <glib-object.h>
> +
> +#define _LIBMM_INSIDE_MM
> +#include <libmm-glib.h>
> +
> +#include "mm-kernel-device.h"
> +
> +#define
> MM_TYPE_KERNEL_DEVICE_GENERIC (mm_kernel_device_generic_ge
> t_type ())
> +#define
> MM_KERNEL_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST
> ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGeneric))
> +#define
> MM_KERNEL_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST
> ((klass), MM_TYPE_KERNEL_DEVICE_GENERIC,
> MMKernelDeviceGenericClass))
> +#define
> MM_IS_KERNEL_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE
> ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC))
> +#define MM_IS_KERNEL_DEVICE_GENERIC_CLASS(klass)
> (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_GENERIC))
> +#define
> MM_KERNEL_DEVICE_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS
> ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericClass))
> +
> +typedef struct _MMKernelDeviceGeneric MMKernelDeviceGeneric;
> +typedef struct
> _MMKernelDeviceGenericClass MMKernelDeviceGenericClass;
> +typedef struct _MMKernelDeviceGenericPrivate
> MMKernelDeviceGenericPrivate;
> +
> +struct _MMKernelDeviceGeneric {
> + MMKernelDevice parent;
> + MMKernelDeviceGenericPrivate *priv;
> +};
> +
> +struct _MMKernelDeviceGenericClass {
> + MMKernelDeviceClass parent;
> +};
> +
> +GType mm_kernel_device_generic_get_type (void);
> +MMKernelDevice
> *mm_kernel_device_generic_new (MMKernelEventProperties *propert
> ies,
> + GError
> **error);
> +
> +#endif /* MM_KERNEL_DEVICE_GENERIC_H */
> diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
> index 85f66a8..991b7e5 100644
> --- a/src/mm-base-manager.c
> +++ b/src/mm-base-manager.c
> @@ -26,6 +26,8 @@
>
> #if WITH_UDEV
> # include "mm-kernel-device-udev.h"
> +#else
> +# include "mm-kernel-device-generic.h"
> #endif
>
> #include <ModemManager.h>
> @@ -246,6 +248,11 @@ device_added (MMBaseManager *manager,
>
> g_return_if_fail (port != NULL);
>
> + mm_dbg ("(%s/%s): adding device at sysfs path: %s",
> + mm_kernel_device_get_subsystem (port),
> + mm_kernel_device_get_name (port),
> + mm_kernel_device_get_sysfs_path (port));
> +
> if (!mm_kernel_device_is_candidate (port, manual_scan)) {
> /* This could mean that device changed, losing its
> ID_MM_CANDIDATE
> * flags (such as Bluetooth RFCOMM devices upon disconnect.
> @@ -275,6 +282,11 @@ device_added (MMBaseManager *manager,
> if (!device) {
> FindDeviceSupportContext *ctx;
>
> + mm_dbg ("(%s/%s): first port in device %s",
> + mm_kernel_device_get_subsystem (port),
> + mm_kernel_device_get_name (port),
> + physdev_uid);
> +
> /* Keep the device listed in the Manager */
> device = mm_device_new (physdev_uid, hotplugged, FALSE);
> g_hash_table_insert (manager->priv->devices,
> @@ -290,7 +302,11 @@ device_added (MMBaseManager *manager,
> device,
> (GAsyncReadyCallback) device_support_check_ready,
> ctx);
> - }
> + } else
> + mm_dbg ("(%s/%s): additional port in device %s",
> + mm_kernel_device_get_subsystem (port),
> + mm_kernel_device_get_name (port),
> + physdev_uid);
>
> /* Grab the port in the existing device. */
> mm_device_grab_port (device, port);
> @@ -425,9 +441,6 @@ mm_base_manager_start (MMBaseManager *manager,
> }
> #else
> mm_dbg ("Unsupported %s device scan...", manual_scan ? "manual"
> : "automatic");
> -
> - /* Make compiler happy */
> - device_added (manager, NULL, FALSE, FALSE);
> #endif
> }
>
> @@ -642,6 +655,128 @@ handle_scan_devices
> (MmGdbusOrgFreedesktopModemManager1 *manager,
> }
>
> /*******************************************************************
> **********/
> +
> +#if !WITH_UDEV
> +
> +typedef struct {
> + MMBaseManager *self;
> + GDBusMethodInvocation *invocation;
> + GVariant *dictionary;
> +} ReportKernelEventContext;
> +
> +static void
> +report_kernel_event_context_free (ReportKernelEventContext *ctx)
> +{
> + g_object_unref (ctx->invocation);
> + g_object_unref (ctx->self);
> + g_variant_unref (ctx->dictionary);
> + g_slice_free (ReportKernelEventContext, ctx);
> +}
> +
> +static void
> +report_kernel_event_auth_ready (MMAuthProvider *authp,
> + GAsyncResult *res,
> + ReportKernelEventContext *ctx)
> +{
> + GError *error = NULL;
> + MMKernelEventProperties *properties = NULL;
> + MMKernelDevice *kernel_device;
> + const gchar *action;
> +
> + if (!mm_auth_provider_authorize_finish (authp, res, &error))
> + goto out;
> +
> + properties = mm_kernel_event_properties_new_from_dictionary
> (ctx->dictionary, &error);
> + if (!properties)
> + goto out;
> +
> + action = mm_kernel_event_properties_get_action (properties);
> + if (g_strcmp0 (action, "add") != 0 && g_strcmp0 (action,
> "remove") != 0) {
> + error = g_error_new_literal (MM_CORE_ERROR,
> MM_CORE_ERROR_INVALID_ARGS, "Invalid action");
> + goto out;
> + }
> +
> + mm_dbg ("Kernel event reported:");
> + mm_dbg (" action: %s", action);
> + if (mm_kernel_event_properties_get_subsystem (properties))
> + mm_dbg (" subsystem: %s",
> mm_kernel_event_properties_get_subsystem (properties));
> + if (mm_kernel_event_properties_get_name (properties))
> + mm_dbg (" name: %s",
> mm_kernel_event_properties_get_name (properties));
> + if (mm_kernel_event_properties_get_driver (properties))
> + mm_dbg (" driver: %s",
> mm_kernel_event_properties_get_driver (properties));
> + if (mm_kernel_event_properties_get_physdev_uid (properties))
> + mm_dbg (" physdev-uid: %s",
> mm_kernel_event_properties_get_physdev_uid (properties));
> + if (mm_kernel_event_properties_get_physdev_vid (properties))
> + mm_dbg (" physdev-vid: 0x%04X",
> mm_kernel_event_properties_get_physdev_vid (properties));
> + if (mm_kernel_event_properties_get_physdev_pid (properties))
> + mm_dbg (" physdev-pid: 0x%04X",
> mm_kernel_event_properties_get_physdev_pid (properties));
> + if (mm_kernel_event_properties_get_candidate (properties))
> + mm_dbg (" candidate: yes");
> +
> + kernel_device = mm_kernel_device_generic_new (properties,
> &error);
> + if (!kernel_device)
> + goto out;
> +
> + if (g_strcmp0 (action, "add") == 0)
> + device_added (ctx->self, kernel_device, TRUE, TRUE);
> + else if (g_strcmp0 (action, "remove") == 0)
> + device_removed (ctx->self, kernel_device);
> + else
> + g_assert_not_reached ();
> + g_object_unref (kernel_device);
> +
> +out:
> + if (error)
> + g_dbus_method_invocation_take_error (ctx->invocation,
> error);
> + else
> + mm_gdbus_org_freedesktop_modem_manager1_complete_report_kern
> el_event (
> + MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
> + ctx->invocation);
> +
> + if (properties)
> + g_object_unref (properties);
> + report_kernel_event_context_free (ctx);
> +}
> +
> +static gboolean
> +handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1
> *manager,
> + GDBusMethodInvocation *invocation,
> + GVariant *dictionary)
> +{
> + ReportKernelEventContext *ctx;
> +
> + ctx = g_slice_new0 (ReportKernelEventContext);
> + ctx->self = g_object_ref (manager);
> + ctx->invocation = g_object_ref (invocation);
> + ctx->dictionary = g_variant_ref (dictionary);
> +
> + mm_auth_provider_authorize (ctx->self->priv->authp,
> + invocation,
> + MM_AUTHORIZATION_MANAGER_CONTROL,
> + ctx->self->priv->authp_cancellable,
> + (GAsyncReadyCallback)report_kernel_e
> vent_auth_ready,
> + ctx);
> + return TRUE;
> +}
> +
> +#else
> +
> +static gboolean
> +handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1
> *manager,
> + GDBusMethodInvocation *invocation,
> + GVariant *dictionary)
> +{
> + g_dbus_method_invocation_return_error (invocation,
> + MM_CORE_ERROR,
> + MM_CORE_ERROR_UNSUPPORTED
> ,
> + "Cannot report kernel
> event: "
> + "udev monitoring already
> in place");
> + return TRUE;
> +}
> +
> +#endif
> +
> +/*******************************************************************
> **********/
> /* Test profile setup */
>
> static gboolean
> @@ -844,6 +979,10 @@ mm_base_manager_init (MMBaseManager *manager)
> "handle-scan-devices",
> G_CALLBACK (handle_scan_devices),
> NULL);
> + g_signal_connect (manager,
> + "handle-report-kernel-event",
> + G_CALLBACK (handle_report_kernel_event),
> + NULL);
> }
>
> static gboolean
More information about the ModemManager-devel
mailing list