Use libusb hotplug API to automatically detect disconnection

Prior to this commit, the libx52_is_connected API was simply checking if
the device handle was non-NULL. However, this was insufficient, since
the device disconnection would not reset the handle, and was relying on
the daemon to manually disconnect.

The libusb hotplug API provides functionality to register a callback on
device insertion/removal. libx52 only registers for removal, and will
automatically disconnect the device on receiving the callback. This also
modifies libx52_is_connected to fallback to checking if the kernel
driver is active if the linked libusb does not support hotplug (unlikely).

Finally, this commit adds support for the new hotplug related functions
to the libusbx52 preload library. While the preload library doesn't
actually support hotplug, it is sufficient to pretend that it does.
reverse-scroll
nirenjan 2021-09-13 20:27:13 -07:00
parent f2884c57b7
commit 627c1fb004
4 changed files with 88 additions and 2 deletions

View File

@ -277,3 +277,25 @@ int libusb_control_transfer(libusb_device_handle *dev_handle,
return 0;
}
int libusb_kernel_driver_active(libusb_device_handle *hdl, int interface_number)
{
return (hdl->dev->ref_count > 0);
}
/* Indicate that the stub library can support hotplug, even though it doesn't */
int libusb_has_capability(uint32_t capability)
{
return capability == LIBUSB_CAP_HAS_HOTPLUG;
}
/* Dummy function to simulate registering callbacks */
int libusb_hotplug_register_callback(libusb_context *ctx,
libusb_hotplug_event events,
libusb_hotplug_flag flags,
int vendor_id, int product_id, int dev_class,
libusb_hotplug_callback_fn cb_fn, void *user_data,
libusb_hotplug_callback_handle *callback_handle)
{
return LIBUSB_SUCCESS;
}

View File

@ -12,7 +12,7 @@ lib_LTLIBRARIES += libx52.la
# See: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
libx52_v_CUR=6
libx52_v_AGE=4
libx52_v_REV=0
libx52_v_REV=1
libx52_la_SOURCES = \
libx52/x52_control.c \
libx52/x52_core.c \

View File

@ -51,6 +51,9 @@ struct libx52_device {
int timezone[X52_MFD_CLOCKS];
libx52_clock_format time_format[X52_MFD_CLOCKS];
libusb_hotplug_callback_handle hotplug_handle;
int handle_registered;
};
/** Flag bits */

View File

@ -42,9 +42,52 @@ static int libx52_device_is_x52pro(uint16_t idProduct)
return (idProduct == X52_PROD_X52PRO);
}
static int _x52_hotplug_callback(libusb_context *ctx,
libusb_device *device,
libusb_hotplug_event event, void *user_data)
{
libx52_device *dev = user_data;
if (dev == NULL) {
return 0;
}
/* Double check that the context matches the libx52 structure */
if (dev->ctx != ctx) {
return 0;
}
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
/*
* Return 1 if we successfully disconnected. This will automatically
* deregister the callback.
*/
return (libx52_disconnect(dev) == LIBX52_SUCCESS);
}
return 0;
}
bool libx52_is_connected(libx52_device *dev)
{
return (dev && dev->hdl);
int rc;
if (!dev) {
return false;
}
if (dev->hdl) {
if (dev->handle_registered) {
return true;
}
/* Check if interface 0 has a kernel driver attached */
rc = libusb_kernel_driver_active(dev->hdl, 0);
return (rc == 1);
}
return false;
}
int libx52_disconnect(libx52_device *dev)
@ -57,6 +100,7 @@ int libx52_disconnect(libx52_device *dev)
libusb_close(dev->hdl);
dev->hdl = NULL;
dev->flags = 0;
dev->handle_registered = 0;
}
return LIBX52_SUCCESS;
@ -112,6 +156,23 @@ int libx52_connect(libx52_device *dev)
return LIBX52_ERROR_NO_DEVICE;
}
/* Setup hotplug callback when this device is disconnected */
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
/*
* Mark if the hotplug callback registered successfully. If it did
* not register, we can use the kernel driver API to determine if
* the device is still connected.
*/
dev->handle_registered = (LIBUSB_SUCCESS ==
libusb_hotplug_register_callback(dev->ctx,
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0,
desc.idVendor, desc.idProduct,
LIBUSB_HOTPLUG_MATCH_ANY,
_x52_hotplug_callback, dev,
&(dev->hotplug_handle)));
}
return LIBX52_SUCCESS;
}