Add support for udev based device discovery

This is primarily useful for SPI devices. These devices sometimes needs
a combination of an SPI and HID device, so discovery is a bit more
complicated.
This commit is contained in:
Matthew Mirvish 2021-04-08 16:46:01 +02:00 committed by Benjamin Berg
parent e95056aa86
commit b0d9d00762
9 changed files with 242 additions and 9 deletions

View file

@ -132,7 +132,9 @@ FpDeviceClass
FpTimeoutFunc FpTimeoutFunc
FpiDeviceAction FpiDeviceAction
FpIdEntry FpIdEntry
FpiDeviceUdevSubtypeFlags
fpi_device_get_usb_device fpi_device_get_usb_device
fpi_device_get_udev_data
fpi_device_get_virtual_env fpi_device_get_virtual_env
fpi_device_get_current_action fpi_device_get_current_action
fpi_device_retry_new fpi_device_retry_new

View file

@ -24,6 +24,18 @@
#include "fpi-device.h" #include "fpi-device.h"
#include <gusb.h> #include <gusb.h>
#include <config.h>
#ifdef HAVE_UDEV
#include <sys/ioctl.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <linux/hidraw.h>
#include <gudev/gudev.h>
#endif
/** /**
* SECTION: fp-context * SECTION: fp-context
* @title: FpContext * @title: FpContext
@ -434,6 +446,103 @@ fp_context_enumerate (FpContext *context)
} }
} }
#ifdef HAVE_UDEV
{
g_autoptr(GUdevClient) udev_client = g_udev_client_new (NULL);
/* This uses a very simple algorithm to allocate devices to drivers and assumes that no two drivers will want the same device. Future improvements
* could add a usb_discover style udev_discover that returns a score, however for internal devices the potential overlap should be very low between
* separate drivers.
*/
g_autoptr(GList) spidev_devices = g_udev_client_query_by_subsystem (udev_client, "spidev");
g_autoptr(GList) hidraw_devices = g_udev_client_query_by_subsystem (udev_client, "hidraw");
/* for each potential driver, try to match all requested resources. */
for (i = 0; i < priv->drivers->len; i++)
{
GType driver = g_array_index (priv->drivers, GType, i);
g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver);
const FpIdEntry *entry;
if (cls->type != FP_DEVICE_TYPE_UDEV)
continue;
for (entry = cls->id_table; entry->udev_types; entry++)
{
GList *matched_spidev = NULL, *matched_hidraw = NULL;
if (entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_SPIDEV)
{
for (matched_spidev = spidev_devices; matched_spidev; matched_spidev = matched_spidev->next)
{
const gchar * sysfs = g_udev_device_get_sysfs_path (matched_spidev->data);
if (!sysfs)
continue;
if (strstr (sysfs, entry->spi_acpi_id))
break;
}
/* If match was not found exit */
if (matched_spidev == NULL)
continue;
}
if (entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_HIDRAW)
{
for (matched_hidraw = hidraw_devices; matched_hidraw; matched_hidraw = matched_hidraw->next)
{
const gchar * devnode = g_udev_device_get_device_file (matched_hidraw->data);
int temp_hid = -1, res;
struct hidraw_devinfo info;
if (!devnode)
continue;
temp_hid = open (devnode, O_RDWR);
if (temp_hid < 0)
continue;
res = ioctl (temp_hid, HIDIOCGRAWINFO, &info);
close (temp_hid);
if (res < 0)
continue;
if (info.vendor == entry->hid_id.vid && info.product == entry->hid_id.pid)
break;
}
/* If match was not found exit */
if (matched_hidraw == NULL)
continue;
}
priv->pending_devices++;
g_async_initable_new_async (driver,
G_PRIORITY_LOW,
priv->cancellable,
async_device_init_done_cb,
context,
"fpi-driver-data", entry->driver_data,
"fpi-udev-data-spidev", (matched_spidev ? g_udev_device_get_device_file (matched_spidev->data) : NULL),
"fpi-udev-data-hidraw", (matched_hidraw ? g_udev_device_get_device_file (matched_hidraw->data) : NULL),
NULL);
/* remove entries from list to avoid conflicts */
if (matched_spidev)
{
g_object_unref (matched_spidev->data);
spidev_devices = g_list_delete_link (spidev_devices, matched_spidev);
}
if (matched_hidraw)
{
g_object_unref (matched_hidraw->data);
hidraw_devices = g_list_delete_link (hidraw_devices, matched_hidraw);
}
}
}
/* free all unused elemnts in both lists */
g_list_foreach (spidev_devices, (GFunc) g_object_unref, NULL);
g_list_foreach (hidraw_devices, (GFunc) g_object_unref, NULL);
}
#endif
while (priv->pending_devices) while (priv->pending_devices)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
} }

View file

@ -28,18 +28,23 @@ typedef struct
GUsbDevice *usb_device; GUsbDevice *usb_device;
const gchar *virtual_env; const gchar *virtual_env;
struct
{
gchar *spidev_path;
gchar *hidraw_path;
} udev_data;
gboolean is_removed; gboolean is_removed;
gboolean is_open; gboolean is_open;
gchar *device_id; gchar *device_id;
gchar *device_name; gchar *device_name;
FpScanType scan_type; FpScanType scan_type;
guint64 driver_data; guint64 driver_data;
gint nr_enroll_stages; gint nr_enroll_stages;
GSList *sources; GSList *sources;
/* We always make sure that only one task is run at a time. */ /* We always make sure that only one task is run at a time. */
FpiDeviceAction current_action; FpiDeviceAction current_action;

View file

@ -50,6 +50,8 @@ enum {
PROP_FINGER_STATUS, PROP_FINGER_STATUS,
PROP_FPI_ENVIRON, PROP_FPI_ENVIRON,
PROP_FPI_USB_DEVICE, PROP_FPI_USB_DEVICE,
PROP_FPI_UDEV_DATA_SPIDEV,
PROP_FPI_UDEV_DATA_HIDRAW,
PROP_FPI_DRIVER_DATA, PROP_FPI_DRIVER_DATA,
N_PROPS N_PROPS
}; };
@ -169,6 +171,8 @@ fp_device_finalize (GObject *object)
g_clear_object (&priv->usb_device); g_clear_object (&priv->usb_device);
g_clear_pointer (&priv->virtual_env, g_free); g_clear_pointer (&priv->virtual_env, g_free);
g_clear_pointer (&priv->udev_data.spidev_path, g_free);
g_clear_pointer (&priv->udev_data.hidraw_path, g_free);
G_OBJECT_CLASS (fp_device_parent_class)->finalize (object); G_OBJECT_CLASS (fp_device_parent_class)->finalize (object);
} }
@ -248,6 +252,20 @@ fp_device_set_property (GObject *object,
g_assert (g_value_get_object (value) == NULL); g_assert (g_value_get_object (value) == NULL);
break; break;
case PROP_FPI_UDEV_DATA_SPIDEV:
if (cls->type == FP_DEVICE_TYPE_UDEV)
priv->udev_data.spidev_path = g_value_dup_string (value);
else
g_assert (g_value_get_string (value) == NULL);
break;
case PROP_FPI_UDEV_DATA_HIDRAW:
if (cls->type == FP_DEVICE_TYPE_UDEV)
priv->udev_data.hidraw_path = g_value_dup_string (value);
else
g_assert (g_value_get_string (value) == NULL);
break;
case PROP_FPI_DRIVER_DATA: case PROP_FPI_DRIVER_DATA:
priv->driver_data = g_value_get_uint64 (value); priv->driver_data = g_value_get_uint64 (value);
break; break;
@ -425,6 +443,32 @@ fp_device_class_init (FpDeviceClass *klass)
"Private: The USB device for the device", "Private: The USB device for the device",
G_USB_TYPE_DEVICE, G_USB_TYPE_DEVICE,
G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
/**
* FpDevice::fpi-udev-data-spidev: (skip)
*
* This property is only for internal purposes.
*
* Stability: private
*/
properties[PROP_FPI_UDEV_DATA_SPIDEV] =
g_param_spec_string ("fpi-udev-data-spidev",
"Udev data: spidev path",
"Private: The path to /dev/spidevN.M",
NULL,
G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
/**
* FpDevice::fpi-udev-data-hidraw: (skip)
*
* This property is only for internal purposes.
*
* Stability: private
*/
properties[PROP_FPI_UDEV_DATA_HIDRAW] =
g_param_spec_string ("fpi-udev-data-hidraw",
"Udev data: hidraw path",
"Private: The path to /dev/hidrawN",
NULL,
G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
/** /**
* FpDevice::fpi-driver-data: (skip) * FpDevice::fpi-driver-data: (skip)
@ -673,6 +717,7 @@ fp_device_open (FpDevice *device,
break; break;
case FP_DEVICE_TYPE_VIRTUAL: case FP_DEVICE_TYPE_VIRTUAL:
case FP_DEVICE_TYPE_UDEV:
break; break;
default: default:

View file

@ -38,10 +38,12 @@ G_DECLARE_DERIVABLE_TYPE (FpDevice, fp_device, FP, DEVICE, GObject)
/** /**
* FpDeviceType: * FpDeviceType:
* @FP_DEVICE_TYPE_VIRTUAL: The device is a virtual device * @FP_DEVICE_TYPE_VIRTUAL: The device is a virtual device
* @FP_DEVICE_TYPE_UDEV: The device is a udev device
* @FP_DEVICE_TYPE_USB: The device is a USB device * @FP_DEVICE_TYPE_USB: The device is a USB device
*/ */
typedef enum { typedef enum {
FP_DEVICE_TYPE_VIRTUAL, FP_DEVICE_TYPE_VIRTUAL,
FP_DEVICE_TYPE_UDEV,
FP_DEVICE_TYPE_USB, FP_DEVICE_TYPE_USB,
} FpDeviceType; } FpDeviceType;

View file

@ -335,6 +335,38 @@ fpi_device_get_usb_device (FpDevice *device)
return priv->usb_device; return priv->usb_device;
} }
/**
* fpi_device_get_udev_data:
* @device: The #FpDevice
* @subtype: Which subtype to get information about
*
* Get a subtype-specific hardware resource for this #FpDevice. Only permissible to call if the
* #FpDevice is of type %FP_DEVICE_TYPE_UDEV.
*
* Returns: Depends on @subtype; for SPIDEV/HIDRAW returns a path to the relevant device.
*/
gpointer
fpi_device_get_udev_data (FpDevice *device, FpiDeviceUdevSubtypeFlags subtype)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_UDEV, NULL);
switch (subtype)
{
case FPI_DEVICE_UDEV_SUBTYPE_HIDRAW:
return priv->udev_data.hidraw_path;
case FPI_DEVICE_UDEV_SUBTYPE_SPIDEV:
return priv->udev_data.spidev_path;
default:
g_return_val_if_reached (NULL);
return NULL;
}
}
/** /**
* fpi_device_get_virtual_env: * fpi_device_get_virtual_env:
* @device: The #FpDevice * @device: The #FpDevice
@ -977,6 +1009,7 @@ fpi_device_close_complete (FpDevice *device, GError *error)
break; break;
case FP_DEVICE_TYPE_VIRTUAL: case FP_DEVICE_TYPE_VIRTUAL:
case FP_DEVICE_TYPE_UDEV:
break; break;
default: default:

View file

@ -24,6 +24,18 @@
#include "fp-image.h" #include "fp-image.h"
#include "fpi-print.h" #include "fpi-print.h"
#include <config.h>
/**
* FpiDeviceUdevSubtype:
* @FPI_DEVICE_UDEV_SUBTYPE_SPIDEV: The device requires an spidev node
* @FPI_DEVICE_UDEV_SUBTYPE_HIDRAW: The device requires a hidraw node
*/
typedef enum {
FPI_DEVICE_UDEV_SUBTYPE_SPIDEV = 1 << 0,
FPI_DEVICE_UDEV_SUBTYPE_HIDRAW = 1 << 1,
} FpiDeviceUdevSubtypeFlags;
/** /**
* FpIdEntry: * FpIdEntry:
* *
@ -43,6 +55,16 @@ struct _FpIdEntry
guint vid; guint vid;
}; };
const gchar *virtual_envvar; const gchar *virtual_envvar;
struct
{
FpiDeviceUdevSubtypeFlags udev_types;
const gchar *spi_acpi_id;
struct
{
guint pid;
guint vid;
} hid_id;
};
}; };
guint64 driver_data; guint64 driver_data;
}; };
@ -171,6 +193,8 @@ typedef enum {
GUsbDevice *fpi_device_get_usb_device (FpDevice *device); GUsbDevice *fpi_device_get_usb_device (FpDevice *device);
const gchar *fpi_device_get_virtual_env (FpDevice *device); const gchar *fpi_device_get_virtual_env (FpDevice *device);
gpointer fpi_device_get_udev_data (FpDevice *device,
FpiDeviceUdevSubtypeFlags subtype);
//const gchar *fpi_device_get_spi_dev (FpDevice *device); //const gchar *fpi_device_get_spi_dev (FpDevice *device);

View file

@ -224,6 +224,7 @@ deps = [
glib_dep, glib_dep,
gobject_dep, gobject_dep,
gusb_dep, gusb_dep,
gudev_dep,
imaging_dep, imaging_dep,
mathlib_dep, mathlib_dep,
nss_dep, nss_dep,

View file

@ -94,6 +94,9 @@ virtual_drivers = [
'virtual_device_storage', 'virtual_device_storage',
] ]
udev_drivers = [
]
default_drivers = [ default_drivers = [
'upektc_img', 'upektc_img',
'vfs5011', 'vfs5011',
@ -126,7 +129,7 @@ endian_independent_drivers = virtual_drivers + [
'synaptics', 'synaptics',
] ]
all_drivers = default_drivers + virtual_drivers all_drivers = default_drivers + virtual_drivers + udev_drivers
if drivers == [ 'all' ] if drivers == [ 'all' ]
drivers = all_drivers drivers = all_drivers
@ -154,6 +157,7 @@ endif
nss_dep = dependency('', required: false) nss_dep = dependency('', required: false)
imaging_dep = dependency('', required: false) imaging_dep = dependency('', required: false)
gudev_dep = dependency('', required: false)
libfprint_conf.set10('HAVE_PIXMAN', false) libfprint_conf.set10('HAVE_PIXMAN', false)
foreach driver: drivers foreach driver: drivers
if driver == 'uru4000' if driver == 'uru4000'
@ -170,6 +174,14 @@ foreach driver: drivers
libfprint_conf.set10('HAVE_PIXMAN', true) libfprint_conf.set10('HAVE_PIXMAN', true)
endif endif
if udev_drivers.contains(driver)
gudev_dep = dependency('gudev-1.0', required: false)
if not gudev_dep.found()
error('udev is required for SPI support')
endif
libfprint_conf.set10('HAVE_UDEV', true)
endif
if not all_drivers.contains(driver) if not all_drivers.contains(driver)
error('Invalid driver \'' + driver + '\'') error('Invalid driver \'' + driver + '\'')
endif endif