| /* -*- 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. |
| * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es> |
| */ |
| |
| #include <string.h> |
| |
| #define _LIBMM_INSIDE_MM |
| #include <libmm-glib.h> |
| |
| #include <ModemManager-tags.h> |
| |
| #include "mm-kernel-device-udev.h" |
| #include "mm-kernel-device-helpers.h" |
| #include "mm-log-object.h" |
| |
| static void initable_iface_init (GInitableIface *iface); |
| |
| G_DEFINE_TYPE_EXTENDED (MMKernelDeviceUdev, mm_kernel_device_udev, MM_TYPE_KERNEL_DEVICE, 0, |
| G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) |
| |
| enum { |
| PROP_0, |
| PROP_UDEV_CLIENT, |
| PROP_UDEV_DEVICE, |
| PROP_PROPERTIES, |
| PROP_LAST |
| }; |
| |
| static GParamSpec *properties[PROP_LAST]; |
| |
| struct _MMKernelDeviceUdevPrivate { |
| GUdevClient *client; |
| GUdevDevice *device; |
| |
| GUdevDevice *interface; |
| GUdevDevice *physdev; |
| guint16 vendor; |
| guint16 product; |
| guint16 subsystem_vendor; |
| guint16 subsystem_device; |
| guint16 revision; |
| gchar *driver; |
| |
| MMKernelEventProperties *properties; |
| }; |
| |
| /*****************************************************************************/ |
| |
| static guint |
| udev_device_get_sysfs_attr_as_hex (GUdevDevice *device, |
| const gchar *attribute) |
| { |
| const gchar *attr; |
| guint val = 0; |
| |
| attr = g_udev_device_get_sysfs_attr (device, attribute); |
| if (attr) |
| mm_get_uint_from_hex_str (attr, &val); |
| return val; |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| preload_contents_other (MMKernelDeviceUdev *self) |
| { |
| g_autofree gchar *lower_device_name = NULL; |
| |
| /* For any other kind of bus (or the absence of one, as in virtual devices), |
| * assume this is a single port device and don't try to match multiple ports |
| * together. Also, obviously, no vendor, product, revision or interface. */ |
| self->priv->driver = g_strdup (g_udev_device_get_driver (self->priv->device)); |
| |
| /* But look for a lower real physical device, as we may have one */ |
| lower_device_name = mm_kernel_device_get_lower_device_name (g_udev_device_get_sysfs_path (self->priv->device)); |
| if (lower_device_name) { |
| g_autoptr(GUdevDevice) lower_device = NULL; |
| const gchar *subsystem; |
| |
| subsystem = g_udev_device_get_subsystem (self->priv->device); |
| lower_device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, lower_device_name); |
| if (!lower_device) { |
| mm_obj_dbg (self, "couldn't find lower device: %s/%s", subsystem, lower_device_name); |
| } else { |
| g_autoptr(MMKernelDevice) lower_kernel_device = NULL; |
| |
| mm_obj_dbg (self, "setting up lower device: %s/%s", subsystem, lower_device_name); |
| lower_kernel_device = mm_kernel_device_udev_new (self->priv->client, lower_device); |
| g_object_set (self, |
| "lower-device", lower_kernel_device, |
| NULL); |
| } |
| } |
| } |
| |
| static void |
| preload_contents_platform (MMKernelDeviceUdev *self, |
| const gchar *platform) |
| { |
| g_autoptr(GUdevDevice) iter = NULL; |
| |
| iter = g_object_ref (self->priv->device); |
| while (iter) { |
| GUdevDevice *parent; |
| |
| /* Store the first driver found */ |
| if (!self->priv->driver) |
| self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); |
| |
| /* Take first parent with the given platform subsystem as physical device */ |
| if (!self->priv->physdev && (g_strcmp0 (g_udev_device_get_subsystem (iter), platform) == 0)) { |
| self->priv->physdev = g_object_ref (iter); |
| /* stop traversing as soon as the physical device is found */ |
| break; |
| } |
| |
| parent = g_udev_device_get_parent (iter); |
| g_clear_object (&iter); |
| iter = parent; |
| } |
| } |
| |
| static void |
| preload_contents_pcmcia (MMKernelDeviceUdev *self) |
| { |
| g_autoptr(GUdevDevice) iter = NULL; |
| gboolean pcmcia_subsystem_found = FALSE; |
| |
| iter = g_object_ref (self->priv->device); |
| while (iter) { |
| g_autoptr(GUdevDevice) parent = NULL; |
| |
| /* Store the first driver found */ |
| if (!self->priv->driver) |
| self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); |
| |
| if (g_strcmp0 (g_udev_device_get_subsystem (iter), "pcmcia") == 0) |
| pcmcia_subsystem_found = TRUE; |
| |
| /* If the parent of this PCMCIA device is no longer part of |
| * the PCMCIA subsystem, we want to stop since we're looking |
| * for the base PCMCIA device, not the PCMCIA controller which |
| * is usually PCI or some other bus type. |
| */ |
| parent = g_udev_device_get_parent (iter); |
| |
| if (pcmcia_subsystem_found && parent && (g_strcmp0 (g_udev_device_get_subsystem (parent), "pcmcia") != 0)) { |
| self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "manf_id"); |
| self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "card_id"); |
| self->priv->physdev = g_object_ref (iter); |
| /* stop traversing as soon as the physical device is found */ |
| break; |
| } |
| |
| g_clear_object (&iter); |
| iter = g_steal_pointer (&parent); |
| } |
| } |
| |
| static void |
| preload_contents_pci (MMKernelDeviceUdev *self) |
| { |
| g_autoptr(GUdevDevice) iter = NULL; |
| |
| iter = g_object_ref (self->priv->device); |
| while (iter) { |
| GUdevDevice *parent; |
| |
| /* Store the first driver found */ |
| if (!self->priv->driver) |
| self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); |
| |
| /* the PCI channel specific devices have their own drivers and |
| * subsystems, we can rely on the physical device being the first |
| * one that reports the 'pci' subsystem */ |
| if (!self->priv->physdev && (g_strcmp0 (g_udev_device_get_subsystem (iter), "pci") == 0)) { |
| self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "vendor"); |
| self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "device"); |
| self->priv->subsystem_vendor = udev_device_get_sysfs_attr_as_hex (iter, "subsystem_vendor"); |
| self->priv->subsystem_device = udev_device_get_sysfs_attr_as_hex (iter, "subsystem_device"); |
| self->priv->revision = udev_device_get_sysfs_attr_as_hex (iter, "revision"); |
| self->priv->physdev = g_object_ref (iter); |
| /* stop traversing as soon as the physical device is found */ |
| break; |
| } |
| |
| parent = g_udev_device_get_parent (iter); |
| g_clear_object (&iter); |
| iter = parent; |
| } |
| } |
| |
| static void |
| preload_contents_usb (MMKernelDeviceUdev *self) |
| { |
| g_autoptr(GUdevDevice) iter = NULL; |
| |
| iter = g_object_ref (self->priv->device); |
| while (iter) { |
| GUdevDevice *parent; |
| const gchar *devtype; |
| |
| devtype = g_udev_device_get_devtype (iter); |
| |
| /* is this the USB interface? */ |
| if (!self->priv->interface && (g_strcmp0 (devtype, "usb_interface") == 0)) { |
| self->priv->interface = g_object_ref (iter); |
| self->priv->driver = g_strdup (g_udev_device_get_driver (iter)); |
| } |
| |
| /* is this the USB physdev? */ |
| if (!self->priv->physdev && (g_strcmp0 (devtype, "usb_device") == 0)) { |
| self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "idVendor"); |
| self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "idProduct"); |
| self->priv->revision = udev_device_get_sysfs_attr_as_hex (iter, "bcdDevice"); |
| self->priv->physdev = g_object_ref (iter); |
| /* stop traversing as soon as the physical device is found */ |
| break; |
| } |
| |
| parent = g_udev_device_get_parent (iter); |
| g_clear_object (&iter); |
| iter = parent; |
| } |
| } |
| |
| static gchar * |
| find_device_bus_subsystem (MMKernelDeviceUdev *self) |
| { |
| g_autoptr(GUdevDevice) iter = NULL; |
| |
| iter = g_object_ref (self->priv->device); |
| while (iter) { |
| const gchar *subsys; |
| GUdevDevice *parent; |
| |
| /* stop search as soon as we find a parent object |
| * of one of the supported bus subsystems */ |
| subsys = g_udev_device_get_subsystem (iter); |
| if ((g_strcmp0 (subsys, "usb") == 0) || |
| (g_strcmp0 (subsys, "pcmcia") == 0) || |
| (g_strcmp0 (subsys, "pci") == 0) || |
| (g_strcmp0 (subsys, "platform") == 0) || |
| (g_strcmp0 (subsys, "pnp") == 0) || |
| (g_strcmp0 (subsys, "sdio") == 0)) |
| return g_strdup (subsys); |
| |
| parent = g_udev_device_get_parent (iter); |
| g_clear_object (&iter); |
| iter = parent; |
| } |
| |
| /* no more parents to check */ |
| return NULL; |
| } |
| |
| static void |
| preload_contents (MMKernelDeviceUdev *self) |
| { |
| g_autofree gchar *bus_subsys = NULL; |
| |
| bus_subsys = find_device_bus_subsystem (self); |
| if (g_strcmp0 (bus_subsys, "usb") == 0) |
| preload_contents_usb (self); |
| else if (g_strcmp0 (bus_subsys, "pcmcia") == 0) |
| preload_contents_pcmcia (self); |
| else if (g_strcmp0 (bus_subsys, "pci") == 0) |
| preload_contents_pci (self); |
| else if ((g_strcmp0 (bus_subsys, "platform") == 0) || |
| (g_strcmp0 (bus_subsys, "pnp") == 0) || |
| (g_strcmp0 (bus_subsys, "sdio") == 0)) |
| preload_contents_platform (self, bus_subsys); |
| else |
| preload_contents_other (self); |
| |
| if (!bus_subsys) |
| return; |
| |
| mm_obj_dbg (self, "port contents loaded:"); |
| mm_obj_dbg (self, " bus: %s", bus_subsys ? bus_subsys : "n/a"); |
| if (self->priv->interface) |
| mm_obj_dbg (self, " interface: %s", g_udev_device_get_sysfs_path (self->priv->interface)); |
| if (self->priv->physdev) |
| mm_obj_dbg (self, " device: %s", g_udev_device_get_sysfs_path (self->priv->physdev)); |
| if (self->priv->driver) |
| mm_obj_dbg (self, " driver: %s", self->priv->driver); |
| if (self->priv->vendor) |
| mm_obj_dbg (self, " vendor: %04x", self->priv->vendor); |
| if (self->priv->product) |
| mm_obj_dbg (self, " product: %04x", self->priv->product); |
| if (self->priv->subsystem_vendor) |
| mm_obj_dbg (self, " subsystem vendor: %04x", self->priv->subsystem_vendor); |
| if (self->priv->subsystem_device) |
| mm_obj_dbg (self, " subsystem device: %04x", self->priv->subsystem_device); |
| if (self->priv->revision) |
| mm_obj_dbg (self, " revision: %04x", self->priv->revision); |
| } |
| |
| /*****************************************************************************/ |
| |
| static const gchar * |
| kernel_device_get_subsystem (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| |
| if (self->priv->device) |
| return g_udev_device_get_subsystem (self->priv->device); |
| |
| g_assert (self->priv->properties); |
| return mm_kernel_event_properties_get_subsystem (self->priv->properties); |
| } |
| |
| static const gchar * |
| kernel_device_get_name (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| |
| if (self->priv->device) |
| return g_udev_device_get_name (self->priv->device); |
| |
| g_assert (self->priv->properties); |
| return mm_kernel_event_properties_get_name (self->priv->properties); |
| } |
| |
| static const gchar * |
| kernel_device_get_driver (MMKernelDevice *self) |
| { |
| return MM_KERNEL_DEVICE_UDEV (self)->priv->driver; |
| } |
| |
| static const gchar * |
| kernel_device_get_sysfs_path (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->device ? |
| g_udev_device_get_sysfs_path (self->priv->device) : |
| NULL); |
| } |
| |
| static const gchar * |
| kernel_device_get_wwandev_sysfs_path (MMKernelDevice *_self) |
| { |
| g_autoptr(GUdevDevice) parent = NULL; |
| MMKernelDeviceUdev *self; |
| const gchar *subsys; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| parent = g_udev_device_get_parent (self->priv->device); |
| |
| if (!parent) |
| return NULL; |
| |
| subsys = g_udev_device_get_subsystem (parent); |
| |
| if (!subsys || g_strcmp0 (subsys, "wwan")) |
| return NULL; |
| |
| return g_udev_device_get_sysfs_path (parent); |
| } |
| |
| static const gchar * |
| kernel_device_get_physdev_uid (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| const gchar *uid = NULL; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| |
| /* Prefer the one coming in the properties, if any */ |
| if (self->priv->properties) { |
| if ((uid = mm_kernel_event_properties_get_uid (self->priv->properties)) != NULL) |
| return uid; |
| } |
| |
| /* Try to load from properties set on the physical device */ |
| if ((uid = mm_kernel_device_get_global_property (_self, ID_MM_PHYSDEV_UID)) != NULL) |
| return uid; |
| |
| /* Use physical device sysfs path, if any */ |
| if (self->priv->physdev && (uid = g_udev_device_get_sysfs_path (self->priv->physdev)) != NULL) |
| return uid; |
| |
| /* If there is no physical device sysfs path, use the device sysfs itself */ |
| g_assert (self->priv->device); |
| return g_udev_device_get_sysfs_path (self->priv->device); |
| } |
| |
| static guint16 |
| kernel_device_get_physdev_vid (MMKernelDevice *self) |
| { |
| return MM_KERNEL_DEVICE_UDEV (self)->priv->vendor; |
| } |
| |
| static guint16 |
| kernel_device_get_physdev_pid (MMKernelDevice *self) |
| { |
| return MM_KERNEL_DEVICE_UDEV (self)->priv->product; |
| } |
| |
| static guint16 |
| kernel_device_get_physdev_subsystem_vid (MMKernelDevice *self) |
| { |
| return MM_KERNEL_DEVICE_UDEV (self)->priv->subsystem_vendor; |
| } |
| |
| static guint16 |
| kernel_device_get_physdev_subsystem_pid (MMKernelDevice *self) |
| { |
| return MM_KERNEL_DEVICE_UDEV (self)->priv->subsystem_device; |
| } |
| |
| static guint16 |
| kernel_device_get_physdev_revision (MMKernelDevice *self) |
| { |
| return MM_KERNEL_DEVICE_UDEV (self)->priv->revision; |
| } |
| |
| static const gchar * |
| kernel_device_get_physdev_sysfs_path (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->physdev ? g_udev_device_get_sysfs_path (self->priv->physdev) : NULL); |
| } |
| |
| static const gchar * |
| kernel_device_get_physdev_subsystem (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->physdev ? g_udev_device_get_subsystem (self->priv->physdev) : NULL); |
| } |
| |
| static const gchar * |
| kernel_device_get_physdev_manufacturer (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->physdev ? g_udev_device_get_sysfs_attr (self->priv->physdev, "manufacturer") : NULL); |
| } |
| |
| static const gchar * |
| kernel_device_get_physdev_product (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->physdev ? g_udev_device_get_sysfs_attr (self->priv->physdev, "product") : NULL); |
| } |
| |
| static gint |
| kernel_device_get_interface_number (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceNumber") : -1); |
| } |
| |
| static gint |
| kernel_device_get_interface_class (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceClass") : -1); |
| } |
| |
| static gint |
| kernel_device_get_interface_subclass (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceSubClass") : -1); |
| } |
| |
| static gint |
| kernel_device_get_interface_protocol (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceProtocol") : -1); |
| } |
| |
| static const gchar * |
| kernel_device_get_interface_sysfs_path (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->interface ? g_udev_device_get_sysfs_path (self->priv->interface) : NULL); |
| } |
| |
| static const gchar * |
| kernel_device_get_interface_description (MMKernelDevice *_self) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->interface ? g_udev_device_get_sysfs_attr (self->priv->interface, "interface") : NULL); |
| } |
| |
| static gboolean |
| kernel_device_cmp (MMKernelDevice *_a, |
| MMKernelDevice *_b) |
| { |
| MMKernelDeviceUdev *a; |
| MMKernelDeviceUdev *b; |
| |
| a = MM_KERNEL_DEVICE_UDEV (_a); |
| b = MM_KERNEL_DEVICE_UDEV (_b); |
| |
| if (a->priv->device && b->priv->device) { |
| if (g_udev_device_has_property (a->priv->device, "DEVPATH_OLD") && |
| g_str_has_suffix (g_udev_device_get_sysfs_path (b->priv->device), |
| g_udev_device_get_property (a->priv->device, "DEVPATH_OLD"))) |
| return TRUE; |
| |
| if (g_udev_device_has_property (b->priv->device, "DEVPATH_OLD") && |
| g_str_has_suffix (g_udev_device_get_sysfs_path (a->priv->device), |
| g_udev_device_get_property (b->priv->device, "DEVPATH_OLD"))) |
| return TRUE; |
| |
| return !g_strcmp0 (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_sysfs_path (b->priv->device)); |
| } |
| |
| 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 gboolean |
| kernel_device_has_property (MMKernelDevice *_self, |
| const gchar *property) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->device ? g_udev_device_has_property (self->priv->device, property) : FALSE); |
| } |
| |
| static const gchar * |
| kernel_device_get_property (MMKernelDevice *_self, |
| const gchar *property) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| return (self->priv->device ? g_udev_device_get_property (self->priv->device, property) : NULL); |
| } |
| |
| static gboolean |
| kernel_device_has_global_property (MMKernelDevice *_self, |
| const gchar *property) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| if (self->priv->physdev && g_udev_device_has_property (self->priv->physdev, property)) |
| return TRUE; |
| |
| return kernel_device_has_property (_self, property); |
| } |
| |
| static const gchar * |
| kernel_device_get_global_property (MMKernelDevice *_self, |
| const gchar *property) |
| { |
| MMKernelDeviceUdev *self; |
| const gchar *str; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| |
| if (self->priv->physdev && |
| g_udev_device_has_property (self->priv->physdev, property) && |
| (str = g_udev_device_get_property (self->priv->physdev, property)) != NULL) |
| return str; |
| |
| return kernel_device_get_property (_self, property); |
| } |
| |
| /*****************************************************************************/ |
| |
| static gboolean |
| kernel_device_has_attribute (MMKernelDevice *_self, |
| const gchar *attribute) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| if (!self->priv->device) |
| return FALSE; |
| |
| return g_udev_device_has_sysfs_attr (self->priv->device, attribute); |
| } |
| |
| static const gchar * |
| kernel_device_get_attribute (MMKernelDevice *_self, |
| const gchar *attribute) |
| { |
| MMKernelDeviceUdev *self; |
| |
| self = MM_KERNEL_DEVICE_UDEV (_self); |
| if (!self->priv->device) |
| return NULL; |
| |
| return g_udev_device_get_sysfs_attr (self->priv->device, attribute); |
| } |
| |
| /*****************************************************************************/ |
| |
| MMKernelDevice * |
| mm_kernel_device_udev_new (GUdevClient *udev_client, |
| GUdevDevice *udev_device) |
| { |
| GError *error = NULL; |
| MMKernelDevice *self; |
| |
| |
| self = MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV, |
| NULL, |
| &error, |
| "udev-client", udev_client, |
| "udev-device", udev_device, |
| NULL)); |
| g_assert_no_error (error); |
| return self; |
| } |
| |
| /*****************************************************************************/ |
| |
| MMKernelDevice * |
| mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client, |
| MMKernelEventProperties *props, |
| GError **error) |
| { |
| return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV, |
| NULL, |
| error, |
| "udev-client", udev_client, |
| "properties", props, |
| NULL)); |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| mm_kernel_device_udev_init (MMKernelDeviceUdev *self) |
| { |
| /* Initialize private data */ |
| self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevPrivate); |
| } |
| |
| static void |
| set_property (GObject *object, |
| guint prop_id, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object); |
| |
| switch (prop_id) { |
| case PROP_UDEV_CLIENT: |
| g_assert (!self->priv->client); |
| self->priv->client = g_value_dup_object (value); |
| break; |
| case PROP_UDEV_DEVICE: |
| g_assert (!self->priv->device); |
| self->priv->device = g_value_dup_object (value); |
| break; |
| case PROP_PROPERTIES: |
| g_assert (!self->priv->properties); |
| self->priv->properties = g_value_dup_object (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) |
| { |
| MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object); |
| |
| switch (prop_id) { |
| case PROP_UDEV_CLIENT: |
| g_value_set_object (value, self->priv->client); |
| break; |
| case PROP_UDEV_DEVICE: |
| g_value_set_object (value, self->priv->device); |
| break; |
| 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) |
| { |
| MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (initable); |
| const gchar *subsystem; |
| const gchar *name; |
| |
| if (!self->priv->client) { |
| g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "missing client in kernel device"); |
| return FALSE; |
| } |
| |
| /* When created from a GUdevDevice, we're done */ |
| if (self->priv->device) { |
| preload_contents (self); |
| return TRUE; |
| } |
| |
| /* Otherwise, we do need properties with subsystem and name */ |
| if (!self->priv->properties) { |
| g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "missing properties in kernel device"); |
| return FALSE; |
| } |
| |
| subsystem = mm_kernel_event_properties_get_subsystem (self->priv->properties); |
| if (!subsystem) { |
| g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "subsystem is mandatory in kernel device"); |
| return FALSE; |
| } |
| |
| name = mm_kernel_event_properties_get_name (self->priv->properties); |
| 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; |
| } |
| |
| /* On remove events, we don't look for the GUdevDevice */ |
| if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove")) { |
| g_assert (!self->priv->device); |
| self->priv->device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, name); |
| if (!self->priv->device) { |
| g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "device %s/%s not found", |
| subsystem, |
| name); |
| return FALSE; |
| } |
| } |
| |
| if (self->priv->device) |
| preload_contents (self); |
| return TRUE; |
| } |
| |
| static void |
| dispose (GObject *object) |
| { |
| MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object); |
| |
| g_clear_pointer (&self->priv->driver, g_free); |
| g_clear_object (&self->priv->physdev); |
| g_clear_object (&self->priv->interface); |
| g_clear_object (&self->priv->device); |
| g_clear_object (&self->priv->client); |
| g_clear_object (&self->priv->properties); |
| |
| G_OBJECT_CLASS (mm_kernel_device_udev_parent_class)->dispose (object); |
| } |
| |
| static void |
| initable_iface_init (GInitableIface *iface) |
| { |
| iface->init = initable_init; |
| } |
| |
| static void |
| mm_kernel_device_udev_class_init (MMKernelDeviceUdevClass *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 (MMKernelDeviceUdevPrivate)); |
| |
| 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_wwandev_sysfs_path = kernel_device_get_wwandev_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_physdev_subsystem_vid = kernel_device_get_physdev_subsystem_vid; |
| kernel_device_class->get_physdev_subsystem_pid = kernel_device_get_physdev_subsystem_pid; |
| kernel_device_class->get_physdev_revision = kernel_device_get_physdev_revision; |
| kernel_device_class->get_physdev_sysfs_path = kernel_device_get_physdev_sysfs_path; |
| kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem; |
| kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer; |
| kernel_device_class->get_physdev_product = kernel_device_get_physdev_product; |
| kernel_device_class->get_interface_number = kernel_device_get_interface_number; |
| kernel_device_class->get_interface_class = kernel_device_get_interface_class; |
| kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass; |
| kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol; |
| kernel_device_class->get_interface_sysfs_path = kernel_device_get_interface_sysfs_path; |
| kernel_device_class->get_interface_description = kernel_device_get_interface_description; |
| 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->has_global_property = kernel_device_has_global_property; |
| kernel_device_class->get_global_property = kernel_device_get_global_property; |
| kernel_device_class->has_attribute = kernel_device_has_attribute; |
| kernel_device_class->get_attribute = kernel_device_get_attribute; |
| |
| properties[PROP_UDEV_DEVICE] = |
| g_param_spec_object ("udev-device", |
| "udev device", |
| "Device object as reported by GUdev", |
| G_UDEV_TYPE_DEVICE, |
| G_PARAM_READWRITE); |
| g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]); |
| |
| properties[PROP_UDEV_CLIENT] = |
| g_param_spec_object ("udev-client", |
| "udev client", |
| "GUdev client", |
| G_UDEV_TYPE_CLIENT, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
| g_object_class_install_property (object_class, PROP_UDEV_CLIENT, properties[PROP_UDEV_CLIENT]); |
| |
| 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]); |
| } |