Add hotplug support.
The internal API is changing as follows: - Adding two new functions. usbi_connect_device, and usbi_disconnect_device. Backends must call these functions to add them to the context's device list at one of two places: initial enumeration (done at init), and on device attach and removal. These functions need to be called once per context. - Backends that support hotplug should not provide a get_device_list funtion. This function is now deprecated and will likely be removed once all backends support hotplug. The external API is changing as follows: - Two new functions have been added to register and deregister callbacks for hotplug notification: libusb_hotplug_register_callback(), libusb_hotplug_deregister_callback(). Hotplug callbacks are called by libusb_handle_events(). Details of the new API can be found in libusb.h. - A new capability check has been added to check for hotplug support. See LIBUSB_CAP_HAS_HOTPLUG. Aa suggested by Xiaofan add new example has been added to show how to use the new external hotplug API. See examples/hotplugtest.c. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
2948008951
commit
7801ff94fa
13 changed files with 773 additions and 37 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -29,6 +29,7 @@ examples/xusb
|
|||
examples/dpfp
|
||||
examples/dpfp_threaded
|
||||
examples/fxload
|
||||
examples/hotplugtest
|
||||
tests/stress
|
||||
*.exe
|
||||
*.pc
|
||||
|
|
|
@ -505,7 +505,7 @@ RECURSIVE = NO
|
|||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||
|
||||
EXCLUDE = @top_srcdir@/libusb/libusbi.h
|
||||
EXCLUDE = @top_srcdir@/libusb/libusbi.h @top_srcdir@/libusb/hotplug.h
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
||||
# directories that are symbolic links (a Unix filesystem feature) are excluded
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
AM_CPPFLAGS = -I$(top_srcdir)/libusb
|
||||
LDADD = ../libusb/libusb-1.0.la
|
||||
|
||||
noinst_PROGRAMS = listdevs xusb fxload
|
||||
noinst_PROGRAMS = listdevs xusb fxload hotplugtest
|
||||
|
||||
if HAVE_SIGACTION
|
||||
noinst_PROGRAMS += dpfp
|
||||
|
|
96
examples/hotplugtest.c
Normal file
96
examples/hotplugtest.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* libusb example program for hotplug API
|
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.ccom>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libusb.h"
|
||||
|
||||
int done = 0;
|
||||
libusb_device_handle *handle;
|
||||
|
||||
static int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data)
|
||||
{
|
||||
struct libusb_device_descriptor desc;
|
||||
int rc;
|
||||
|
||||
rc = libusb_get_device_descriptor(dev, &desc);
|
||||
if (LIBUSB_SUCCESS != rc) {
|
||||
fprintf (stderr, "Error getting device descriptor\n");
|
||||
}
|
||||
|
||||
printf ("Device attached: %04x:%04x\n", desc.idVendor, desc.idProduct);
|
||||
|
||||
libusb_open (dev, &handle);
|
||||
|
||||
done++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LIBUSB_CALL hotplug_callback_detach(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data)
|
||||
{
|
||||
printf ("Device detached\n");
|
||||
|
||||
libusb_close (handle);
|
||||
|
||||
done++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
libusb_hotplug_callback_handle hp[2];
|
||||
int product_id, vendor_id, class_id;
|
||||
int rc;
|
||||
|
||||
vendor_id = (argc > 1) ? strtol (argv[1], NULL, 0) : 0x045a;
|
||||
product_id = (argc > 2) ? strtol (argv[2], NULL, 0) : 0x5005;
|
||||
class_id = (argc > 3) ? strtol (argv[3], NULL, 0) : LIBUSB_HOTPLUG_MATCH_ANY;
|
||||
|
||||
libusb_init (NULL);
|
||||
|
||||
if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
printf ("Hotplug capabilites are not supported on this platform\n");
|
||||
libusb_exit (NULL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 0, vendor_id,
|
||||
product_id, class_id, hotplug_callback, NULL, &hp[0]);
|
||||
if (LIBUSB_SUCCESS != rc) {
|
||||
fprintf (stderr, "Error registering callback 0\n");
|
||||
libusb_exit (NULL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, vendor_id,
|
||||
product_id,class_id, hotplug_callback_detach, NULL, &hp[1]);
|
||||
if (LIBUSB_SUCCESS != rc) {
|
||||
fprintf (stderr, "Error registering callback 1\n");
|
||||
libusb_exit (NULL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (done < 2) {
|
||||
libusb_handle_events (NULL);
|
||||
}
|
||||
|
||||
libusb_exit (NULL);
|
||||
}
|
|
@ -50,10 +50,8 @@ endif
|
|||
libusb_1_0_la_CFLAGS = $(AM_CFLAGS)
|
||||
libusb_1_0_la_LDFLAGS = $(LTLDFLAGS)
|
||||
libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c $(OS_SRC) \
|
||||
os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h \
|
||||
$(THREADS_SRC) \
|
||||
os/poll_posix.h os/poll_windows.h \
|
||||
os/windows_common.h
|
||||
os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h os/windows_common.h \
|
||||
hotplug.h hotplug.c $(THREADS_SRC) os/poll_posix.h os/poll_windows.h
|
||||
|
||||
hdrdir = $(includedir)/libusb-1.0
|
||||
hdr_HEADERS = libusb.h
|
||||
|
|
147
libusb/core.c
147
libusb/core.c
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Core functions for libusbx
|
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@cs.unm.edu>
|
||||
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
|
||||
*
|
||||
|
@ -33,6 +34,7 @@
|
|||
#endif
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
|
||||
|
@ -90,6 +92,7 @@ struct list_head active_contexts_list;
|
|||
* usually won't need to thread)
|
||||
* - Lightweight with lean API
|
||||
* - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer
|
||||
* - Hotplug support (see \ref hotplug)
|
||||
*
|
||||
* \section gettingstarted Getting Started
|
||||
*
|
||||
|
@ -191,19 +194,6 @@ struct list_head active_contexts_list;
|
|||
* - Clearing of halt/stall condition (libusb_clear_halt())
|
||||
* - Device resets (libusb_reset_device())
|
||||
*
|
||||
* \section nohotplug No hotplugging
|
||||
*
|
||||
* libusbx-1.0 lacks functionality for providing notifications of when devices
|
||||
* are added or removed. This functionality is planned to be implemented
|
||||
* in a later version of libusbx.
|
||||
*
|
||||
* That said, there is basic disconnection handling for open device handles:
|
||||
* - If there are ongoing transfers, libusbx's handle_events loop will detect
|
||||
* disconnections and complete ongoing transfers with the
|
||||
* LIBUSB_TRANSFER_NO_DEVICE status code.
|
||||
* - Many functions such as libusb_set_configuration() return the special
|
||||
* LIBUSB_ERROR_NO_DEVICE error code when the device has been disconnected.
|
||||
*
|
||||
* \section configsel Configuration selection and handling
|
||||
*
|
||||
* When libusbx presents a device handle to an application, there is a chance
|
||||
|
@ -525,12 +515,65 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
|
|||
dev->speed = LIBUSB_SPEED_UNKNOWN;
|
||||
memset(&dev->os_priv, 0, priv_size);
|
||||
|
||||
usbi_mutex_lock(&ctx->usb_devs_lock);
|
||||
list_add(&dev->list, &ctx->usb_devs);
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
usbi_connect_device (dev);
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void usbi_connect_device(struct libusb_device *dev)
|
||||
{
|
||||
libusb_hotplug_message message;
|
||||
ssize_t ret;
|
||||
|
||||
message.event = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED;
|
||||
message.device = dev;
|
||||
dev->attached = 1;
|
||||
|
||||
usbi_mutex_lock(&dev->ctx->usb_devs_lock);
|
||||
list_add(&dev->list, &dev->ctx->usb_devs);
|
||||
usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
|
||||
|
||||
/* Signal that an event has occurred for this device if we support hotplug AND
|
||||
* the hotplug pipe is ready. This prevents an event from getting raised during
|
||||
* initial enumeration. */
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_pipe[1] > 0) {
|
||||
ret = usbi_write(dev->ctx->hotplug_pipe[1], &message, sizeof(message));
|
||||
if (sizeof (message) != ret) {
|
||||
usbi_err(DEVICE_CTX(dev), "error writing hotplug message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usbi_disconnect_device(struct libusb_device *dev)
|
||||
{
|
||||
libusb_hotplug_message message;
|
||||
struct libusb_context *ctx = dev->ctx;
|
||||
ssize_t ret;
|
||||
|
||||
message.event = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
|
||||
message.device = dev;
|
||||
usbi_mutex_lock(&dev->lock);
|
||||
dev->attached = 0;
|
||||
usbi_mutex_unlock(&dev->lock);
|
||||
|
||||
/* Signal that an event has occurred for this device if we support hotplug AND
|
||||
* the hotplug pipe is ready. This prevents an event from getting raised during
|
||||
* initial enumeration. libusb_handle_events will take care of dereferencing the
|
||||
* device. */
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_pipe[1] > 0) {
|
||||
ret = usbi_write(dev->ctx->hotplug_pipe[1], &message, sizeof(message));
|
||||
if (sizeof(message) != ret) {
|
||||
usbi_err(DEVICE_CTX(dev), "error writing hotplug message");
|
||||
}
|
||||
}
|
||||
|
||||
usbi_mutex_lock(&ctx->usb_devs_lock);
|
||||
list_del(&dev->list);
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
}
|
||||
|
||||
/* Perform some final sanity checks on a newly discovered device. If this
|
||||
* function fails (negative return code), the device should not be added
|
||||
* to the discovered device list. */
|
||||
|
@ -607,7 +650,25 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
|
|||
if (!discdevs)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
/* backend provides hotplug support */
|
||||
struct libusb_device *dev;
|
||||
|
||||
usbi_mutex_lock(&ctx->usb_devs_lock);
|
||||
list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
|
||||
discdevs = discovered_devs_append(discdevs, dev);
|
||||
|
||||
if (!discdevs) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
} else {
|
||||
/* backend does not provide hotplug support */
|
||||
r = usbi_backend->get_device_list(ctx, &discdevs);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
len = r;
|
||||
goto out;
|
||||
|
@ -910,9 +971,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
|
|||
if (usbi_backend->destroy_device)
|
||||
usbi_backend->destroy_device(dev);
|
||||
|
||||
usbi_mutex_lock(&dev->ctx->usb_devs_lock);
|
||||
list_del(&dev->list);
|
||||
usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
/* backend does not support hotplug */
|
||||
usbi_disconnect_device(dev);
|
||||
}
|
||||
|
||||
usbi_mutex_destroy(&dev->lock);
|
||||
free(dev);
|
||||
|
@ -991,6 +1053,10 @@ int API_EXPORTED libusb_open(libusb_device *dev,
|
|||
int r;
|
||||
usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
|
||||
|
||||
if (!dev->attached) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
_handle = malloc(sizeof(*_handle) + priv_size);
|
||||
if (!_handle)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
@ -1347,6 +1413,9 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev,
|
|||
if (interface_number >= USB_MAXINTERFACES)
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
|
||||
if (!dev->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
usbi_mutex_lock(&dev->lock);
|
||||
if (dev->claimed_interfaces & (1 << interface_number))
|
||||
goto out;
|
||||
|
@ -1429,6 +1498,11 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev,
|
|||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
|
||||
usbi_mutex_lock(&dev->lock);
|
||||
if (!dev->dev->attached) {
|
||||
usbi_mutex_unlock(&dev->lock);
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
if (!(dev->claimed_interfaces & (1 << interface_number))) {
|
||||
usbi_mutex_unlock(&dev->lock);
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
|
@ -1459,6 +1533,9 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev,
|
|||
unsigned char endpoint)
|
||||
{
|
||||
usbi_dbg("endpoint %x", endpoint);
|
||||
if (!dev->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
return usbi_backend->clear_halt(dev, endpoint);
|
||||
}
|
||||
|
||||
|
@ -1484,6 +1561,9 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev,
|
|||
int API_EXPORTED libusb_reset_device(libusb_device_handle *dev)
|
||||
{
|
||||
usbi_dbg("");
|
||||
if (!dev->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
return usbi_backend->reset_device(dev);
|
||||
}
|
||||
|
||||
|
@ -1508,6 +1588,10 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev,
|
|||
int interface_number)
|
||||
{
|
||||
usbi_dbg("interface %d", interface_number);
|
||||
|
||||
if (!dev->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->kernel_driver_active)
|
||||
return usbi_backend->kernel_driver_active(dev, interface_number);
|
||||
else
|
||||
|
@ -1539,6 +1623,10 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev,
|
|||
int interface_number)
|
||||
{
|
||||
usbi_dbg("interface %d", interface_number);
|
||||
|
||||
if (!dev->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->detach_kernel_driver)
|
||||
return usbi_backend->detach_kernel_driver(dev, interface_number);
|
||||
else
|
||||
|
@ -1569,6 +1657,10 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev,
|
|||
int interface_number)
|
||||
{
|
||||
usbi_dbg("interface %d", interface_number);
|
||||
|
||||
if (!dev->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->attach_kernel_driver)
|
||||
return usbi_backend->attach_kernel_driver(dev, interface_number);
|
||||
else
|
||||
|
@ -1667,17 +1759,19 @@ int API_EXPORTED libusb_init(libusb_context **context)
|
|||
usbi_dbg("libusbx v%d.%d.%d.%d", libusb_version_internal.major, libusb_version_internal.minor,
|
||||
libusb_version_internal.micro, libusb_version_internal.nano);
|
||||
|
||||
usbi_mutex_init(&ctx->usb_devs_lock, NULL);
|
||||
usbi_mutex_init(&ctx->open_devs_lock, NULL);
|
||||
usbi_mutex_init(&ctx->hotplug_cbs_lock, NULL);
|
||||
list_init(&ctx->usb_devs);
|
||||
list_init(&ctx->open_devs);
|
||||
list_init(&ctx->hotplug_cbs);
|
||||
|
||||
if (usbi_backend->init) {
|
||||
r = usbi_backend->init(ctx);
|
||||
if (r)
|
||||
goto err_free_ctx;
|
||||
}
|
||||
|
||||
usbi_mutex_init(&ctx->usb_devs_lock, NULL);
|
||||
usbi_mutex_init(&ctx->open_devs_lock, NULL);
|
||||
list_init(&ctx->usb_devs);
|
||||
list_init(&ctx->open_devs);
|
||||
|
||||
r = usbi_io_init(ctx);
|
||||
if (r < 0) {
|
||||
if (usbi_backend->exit)
|
||||
|
@ -1740,6 +1834,8 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
|
|||
list_del (&ctx->list);
|
||||
usbi_mutex_static_unlock(&active_contexts_lock);
|
||||
|
||||
usbi_hotplug_deregister_all(ctx);
|
||||
|
||||
/* a little sanity check. doesn't bother with open_devs locking because
|
||||
* unless there is an application bug, nobody will be accessing this. */
|
||||
if (!list_empty(&ctx->open_devs))
|
||||
|
@ -1751,6 +1847,7 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
|
|||
|
||||
usbi_mutex_destroy(&ctx->open_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->usb_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
|
@ -1767,6 +1864,8 @@ int API_EXPORTED libusb_has_capability(uint32_t capability)
|
|||
switch (capability) {
|
||||
case LIBUSB_CAP_HAS_CAPABILITY:
|
||||
return 1;
|
||||
case LIBUSB_CAP_HAS_HOTPLUG:
|
||||
return !(usbi_backend->get_device_list);
|
||||
case LIBUSB_CAP_HAS_HID_ACCESS:
|
||||
return (usbi_backend->caps & USBI_CAP_HAS_HID_ACCESS);
|
||||
case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER:
|
||||
|
|
302
libusb/hotplug.c
Normal file
302
libusb/hotplug.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
|
||||
/*
|
||||
* Hotplug functions for libusbx
|
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
|
||||
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
|
||||
/**
|
||||
* @defgroup hotplug Device hotplug event notification
|
||||
* This page details how to use the libusb hotplug interface.
|
||||
*
|
||||
* \page hotplug Device hotplug event notification
|
||||
*
|
||||
* \section intro Introduction
|
||||
*
|
||||
* Releases of libusb 1.0 newer than 1.X have added support for hotplug
|
||||
* events. This interface allows you to request notification for the
|
||||
* arrival and departure of matching USB devices.
|
||||
*
|
||||
* To receive hotplug notification you register a callback by calling
|
||||
* libusb_hotplug_register_callback(). This function will optionally return
|
||||
* a handle that can be passed to libusb_hotplug_deregister_callback().
|
||||
*
|
||||
* A callback function must return an int (0 or 1) indicating whether the callback is
|
||||
* expecting additional events. Returning 0 will rearm the callback and 1 will cause
|
||||
* the callback to be deregistered.
|
||||
*
|
||||
* Callbacks for a particulat context are automatically deregistered by libusb_exit().
|
||||
*
|
||||
* As of 1.X there are two supported hotplug events:
|
||||
* - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use
|
||||
* - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available
|
||||
*
|
||||
* A hotplug event can listen for either or both of these events.
|
||||
*
|
||||
* Note: If you receive notification that a device has left and you have any
|
||||
* a libusb_device_handles for the device it is up to you to call libusb_close()
|
||||
* on each handle to free up any remaining resources associated with the device.
|
||||
* Once a device has left any libusb_device_handle associated with the device
|
||||
* are invalid and will remain so even if the device comes back.
|
||||
*
|
||||
* When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered
|
||||
* safe to call any libusbx function that takes a libusb_device. On the other hand,
|
||||
* when handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function
|
||||
* is libusb_get_device_descriptor().
|
||||
*
|
||||
* The following code provides an example of the usage of the hotplug interface:
|
||||
\code
|
||||
static int count = 0;
|
||||
|
||||
int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event, void *user_data) {
|
||||
static libusb_device_handle *handle = NULL;
|
||||
struct libusb_device_descriptor desc;
|
||||
int rc;
|
||||
|
||||
(void)libusb_get_device_descriptor(dev, &desc);
|
||||
|
||||
if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
|
||||
rc = libusb_open(dev, &handle);
|
||||
if (LIBUSB_SUCCESS != rc) {
|
||||
printf("Could not open USB device\n");
|
||||
}
|
||||
} else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
|
||||
if (handle) {
|
||||
libusb_close(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
} else {
|
||||
printf("Unhandled event %d\n", event);
|
||||
}
|
||||
count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
libusb_hotplug_callback_handle handle;
|
||||
int rc;
|
||||
|
||||
libusb_init(NULL);
|
||||
|
||||
rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005,
|
||||
LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL,
|
||||
&handle);
|
||||
if (LIBUSB_SUCCESS != rc) {
|
||||
printf("Error creating a hotplug callback\n");
|
||||
libusb_exit(NULL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (count < 2) {
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
libusb_hotplug_deregister_callback(handle);
|
||||
libusb_exit(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
static int usbi_hotplug_match_cb (struct libusb_device *dev, libusb_hotplug_event event,
|
||||
struct libusb_hotplug_callback *hotplug_cb)
|
||||
{
|
||||
struct libusb_context *ctx = dev->ctx;
|
||||
|
||||
/* Handle lazy deregistration of callback */
|
||||
if (hotplug_cb->needs_free) {
|
||||
/* Free callback */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(hotplug_cb->events & event)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id &&
|
||||
hotplug_cb->vendor_id != dev->device_descriptor.idVendor) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id &&
|
||||
hotplug_cb->product_id != dev->device_descriptor.idProduct) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class &&
|
||||
hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hotplug_cb->cb (ctx == usbi_default_context ? NULL : ctx,
|
||||
dev, event, hotplug_cb->user_data);
|
||||
}
|
||||
|
||||
void usbi_hotplug_match(struct libusb_device *dev, libusb_hotplug_event event)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb, *next;
|
||||
struct libusb_context *ctx = dev->ctx;
|
||||
int ret;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
ret = usbi_hotplug_match_cb (dev, event, hotplug_cb);
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
if (ret) {
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
/* loop through and disconnect all open handles for this device */
|
||||
if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
|
||||
struct libusb_device_handle *handle;
|
||||
|
||||
usbi_mutex_lock(&ctx->open_devs_lock);
|
||||
list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
|
||||
if (dev == handle->dev) {
|
||||
usbi_handle_disconnect (handle);
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->open_devs_lock);
|
||||
}
|
||||
}
|
||||
|
||||
int API_EXPORTED 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 *handle)
|
||||
{
|
||||
libusb_hotplug_callback *new_callback;
|
||||
static int handle_id = 1;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* check for sane values */
|
||||
if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
|
||||
!cb_fn) {
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
USBI_GET_CONTEXT(ctx);
|
||||
|
||||
new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback));
|
||||
if (!new_callback) {
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
new_callback->ctx = ctx;
|
||||
new_callback->vendor_id = vendor_id;
|
||||
new_callback->product_id = product_id;
|
||||
new_callback->dev_class = dev_class;
|
||||
new_callback->flags = flags;
|
||||
new_callback->events = events;
|
||||
new_callback->cb = cb_fn;
|
||||
new_callback->user_data = user_data;
|
||||
new_callback->needs_free = 0;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
/* protect the handle by the context hotplug lock. it doesn't matter if the same handle
|
||||
* is used for different contexts only that the handle is unique for this context */
|
||||
new_callback->handle = handle_id++;
|
||||
|
||||
list_add(&new_callback->list, &ctx->hotplug_cbs);
|
||||
|
||||
if (flags & LIBUSB_HOTPLUG_ENUMERATE) {
|
||||
struct libusb_device *dev;
|
||||
|
||||
usbi_mutex_lock(&ctx->usb_devs_lock);
|
||||
|
||||
list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
|
||||
(void) usbi_hotplug_match_cb (dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, new_callback);
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
if (handle) {
|
||||
*handle = new_callback->handle;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx,
|
||||
libusb_hotplug_callback_handle handle)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
return;
|
||||
}
|
||||
|
||||
USBI_GET_CONTEXT(ctx);
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list,
|
||||
struct libusb_hotplug_callback) {
|
||||
if (handle == hotplug_cb->handle) {
|
||||
/* Mark this callback for deregistration */
|
||||
hotplug_cb->needs_free = 1;
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
}
|
||||
|
||||
void usbi_hotplug_deregister_all(struct libusb_context *ctx) {
|
||||
struct libusb_hotplug_callback *hotplug_cb, *next;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list,
|
||||
struct libusb_hotplug_callback) {
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
}
|
81
libusb/hotplug.h
Normal file
81
libusb/hotplug.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
|
||||
/*
|
||||
* Hotplug support for libusbx
|
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
|
||||
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if !defined(USBI_HOTPLUG_H)
|
||||
#define USBI_HOTPLUG_H
|
||||
|
||||
#ifndef LIBUSBI_H
|
||||
#include "libusbi.h"
|
||||
#endif
|
||||
|
||||
/** \ingroup hotplug
|
||||
* The hotplug callback structure. The user populates this structure with
|
||||
* libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback()
|
||||
* to receive notification of hotplug events.
|
||||
*/
|
||||
struct libusb_hotplug_callback {
|
||||
/** Context this callback is associated with */
|
||||
struct libusb_context *ctx;
|
||||
|
||||
/** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
|
||||
int vendor_id;
|
||||
|
||||
/** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
|
||||
int product_id;
|
||||
|
||||
/** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */
|
||||
int dev_class;
|
||||
|
||||
/** Hotplug callback flags */
|
||||
libusb_hotplug_flag flags;
|
||||
|
||||
/** Event(s) that will trigger this callback */
|
||||
libusb_hotplug_event events;
|
||||
|
||||
/** Callback function to invoke for matching event/device */
|
||||
libusb_hotplug_callback_fn cb;
|
||||
|
||||
/** Handle for this callback (used to match on deregister) */
|
||||
libusb_hotplug_callback_handle handle;
|
||||
|
||||
/** User data that will be passed to the callback function */
|
||||
void *user_data;
|
||||
|
||||
/** Callback is marked for deletion */
|
||||
int needs_free;
|
||||
|
||||
/** List this callback is registered in (ctx->hotplug_cbs) */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
typedef struct libusb_hotplug_callback libusb_hotplug_callback;
|
||||
|
||||
struct libusb_hotplug_message {
|
||||
libusb_hotplug_event event;
|
||||
struct libusb_device *device;
|
||||
};
|
||||
|
||||
typedef struct libusb_hotplug_message libusb_hotplug_message;
|
||||
|
||||
void usbi_hotplug_deregister_all(struct libusb_context *ctx);
|
||||
void usbi_hotplug_match(struct libusb_device *dev, libusb_hotplug_event event);
|
||||
|
||||
#endif
|
55
libusb/io.c
55
libusb/io.c
|
@ -24,6 +24,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#ifndef OS_WINDOWS
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
@ -35,6 +38,7 @@
|
|||
#endif
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
|
||||
/**
|
||||
* \page io Synchronous and asynchronous device I/O
|
||||
|
@ -1070,6 +1074,20 @@ int usbi_io_init(struct libusb_context *ctx)
|
|||
if (r < 0)
|
||||
goto err_close_pipe;
|
||||
|
||||
/* create hotplug pipe */
|
||||
r = usbi_pipe(ctx->hotplug_pipe);
|
||||
if (r < 0) {
|
||||
r = LIBUSB_ERROR_OTHER;
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
fcntl(ctx->hotplug_pipe[1], F_SETFD, O_NONBLOCK);
|
||||
#endif
|
||||
r = usbi_add_pollfd(ctx, ctx->hotplug_pipe[0], POLLIN);
|
||||
if (r < 0)
|
||||
goto err_close_hp_pipe;
|
||||
|
||||
#ifdef USBI_TIMERFD_AVAILABLE
|
||||
ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(),
|
||||
TFD_NONBLOCK);
|
||||
|
@ -1079,7 +1097,7 @@ int usbi_io_init(struct libusb_context *ctx)
|
|||
if (r < 0) {
|
||||
usbi_remove_pollfd(ctx, ctx->ctrl_pipe[0]);
|
||||
close(ctx->timerfd);
|
||||
goto err_close_pipe;
|
||||
goto err_close_hp_pipe;
|
||||
}
|
||||
} else {
|
||||
usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno);
|
||||
|
@ -1089,6 +1107,9 @@ int usbi_io_init(struct libusb_context *ctx)
|
|||
|
||||
return 0;
|
||||
|
||||
err_close_hp_pipe:
|
||||
usbi_close(ctx->hotplug_pipe[0]);
|
||||
usbi_close(ctx->hotplug_pipe[1]);
|
||||
err_close_pipe:
|
||||
usbi_close(ctx->ctrl_pipe[0]);
|
||||
usbi_close(ctx->ctrl_pipe[1]);
|
||||
|
@ -1107,6 +1128,9 @@ void usbi_io_exit(struct libusb_context *ctx)
|
|||
usbi_remove_pollfd(ctx, ctx->ctrl_pipe[0]);
|
||||
usbi_close(ctx->ctrl_pipe[0]);
|
||||
usbi_close(ctx->ctrl_pipe[1]);
|
||||
usbi_remove_pollfd(ctx, ctx->hotplug_pipe[0]);
|
||||
usbi_close(ctx->hotplug_pipe[0]);
|
||||
usbi_close(ctx->hotplug_pipe[1]);
|
||||
#ifdef USBI_TIMERFD_AVAILABLE
|
||||
if (usbi_using_timerfd(ctx)) {
|
||||
usbi_remove_pollfd(ctx, ctx->timerfd);
|
||||
|
@ -1913,9 +1937,32 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
|
|||
}
|
||||
}
|
||||
|
||||
/* fd[1] is always the hotplug pipe */
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && fds[1].revents) {
|
||||
libusb_hotplug_message message;
|
||||
ssize_t ret;
|
||||
|
||||
/* read the message from the hotplug thread */
|
||||
ret = usbi_read(ctx->hotplug_pipe[0], &message, sizeof (message));
|
||||
if (ret < sizeof(message)) {
|
||||
ret = LIBUSB_ERROR_OTHER;
|
||||
goto handled;
|
||||
}
|
||||
|
||||
usbi_hotplug_match(message.device, message.event);
|
||||
|
||||
/* the device left. dereference the device */
|
||||
if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message.event)
|
||||
libusb_unref_device(message.device);
|
||||
|
||||
fds[1].revents = 0;
|
||||
if (1 == r--)
|
||||
goto handled;
|
||||
} /* else there shouldn't be anything on this pipe */
|
||||
|
||||
#ifdef USBI_TIMERFD_AVAILABLE
|
||||
/* on timerfd configurations, fds[1] is the timerfd */
|
||||
if (usbi_using_timerfd(ctx) && fds[1].revents) {
|
||||
/* on timerfd configurations, fds[2] is the timerfd */
|
||||
if (usbi_using_timerfd(ctx) && fds[2].revents) {
|
||||
/* timerfd indicates that a timeout has expired */
|
||||
int ret;
|
||||
usbi_dbg("timerfd triggered");
|
||||
|
@ -1932,7 +1979,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
|
|||
} else {
|
||||
/* more events pending...
|
||||
* prevent OS backend from trying to handle events on timerfd */
|
||||
fds[1].revents = 0;
|
||||
fds[2].revents = 0;
|
||||
r--;
|
||||
}
|
||||
}
|
||||
|
|
103
libusb/libusb.h
103
libusb/libusb.h
|
@ -3,6 +3,7 @@
|
|||
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
|
||||
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright © 2012 Pete Batard <pete@akeo.ie>
|
||||
* Copyright © 2012 Nathan Hjelm <hjelmn@cs.unm.edu>
|
||||
* For more information, please visit: http://libusbx.org
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -682,6 +683,7 @@ struct libusb_control_setup {
|
|||
struct libusb_context;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
struct libusb_hotplug_callback;
|
||||
|
||||
/** \ingroup lib
|
||||
* Structure providing the version of the libusbx runtime
|
||||
|
@ -1515,6 +1517,107 @@ void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx,
|
|||
libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
|
||||
void *user_data);
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Callback handle.
|
||||
*
|
||||
* Callbacks handles are generated by libusb_hotplug_register_callback()
|
||||
* and can be used to deregister callbacks. Callback handles are unique
|
||||
* per libusb_context and it is safe to call libusb_hotplug_deregister_callback()
|
||||
* on an already deregisted callback.
|
||||
*
|
||||
* For more information, see \ref hotplug.
|
||||
*/
|
||||
typedef int libusb_hotplug_callback_handle;
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Flags for hotplug events */
|
||||
typedef enum {
|
||||
/** Arm the callback and fire it for all matching currently attached devices. */
|
||||
LIBUSB_HOTPLUG_ENUMERATE = 1,
|
||||
} libusb_hotplug_flag;
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Hotplug events */
|
||||
typedef enum {
|
||||
/** A device has been plugged in and is ready to use */
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01,
|
||||
|
||||
/** A device has left and is no longer available.
|
||||
* It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
|
||||
* It is safe to call libusb_get_device_descriptor on a device that has left */
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02,
|
||||
} libusb_hotplug_event;
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Wildcard matching for hotplug events */
|
||||
#define LIBUSB_HOTPLUG_MATCH_ANY -1
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Hotplug callback function type. When requesting hotplug event notifications,
|
||||
* you pass a pointer to a callback function of this type.
|
||||
*
|
||||
* This callback may be called by an internal event thread and as such it is
|
||||
* recommended the callback do minimal processing before returning.
|
||||
*
|
||||
* libusbx will call this function later, when a matching event had happened on
|
||||
* a matching device. See \ref hotplug for more information.
|
||||
*
|
||||
* It is safe to call either libusb_hotplug_register_callback() or
|
||||
* libusb_hotplug_deregister_callback() from within a callback function.
|
||||
*
|
||||
* \param libusb_context context of this notification
|
||||
* \param device libusb_device this event occurred on
|
||||
* \param event event that occurred
|
||||
* \param user_data user data provided when this callback was registered
|
||||
* \returns bool whether this callback is finished processing events.
|
||||
* returning 1 will cause this callback to be deregistered
|
||||
*/
|
||||
typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
|
||||
libusb_device *device,
|
||||
libusb_hotplug_event event,
|
||||
void *user_data);
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Register a hotplug callback function
|
||||
*
|
||||
* Register a callback with the libusb_context. The callback will fire
|
||||
* when a matching event occurs on a matching device. The callback is
|
||||
* armed until either it is deregistered with libusb_hotplug_deregister_callback()
|
||||
* or the supplied callback returns 1 to indicate it is finished processing events.
|
||||
*
|
||||
* \param[in] ctx context to register this callback with
|
||||
* \param[in] events bitwise or of events that will trigger this callback. See \ref
|
||||
* libusb_hotplug_event
|
||||
* \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag
|
||||
* \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
|
||||
* \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
|
||||
* \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
|
||||
* \param[in] cb_fn the function to be invoked on a matching event/device
|
||||
* \param[in] user_data user data to pass to the callback function
|
||||
* \param[out] handle pointer to store the handle of the allocated callback (can be NULL)
|
||||
* \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
|
||||
*/
|
||||
int LIBUSB_CALL 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 *handle);
|
||||
|
||||
/** \ingroup hotplug
|
||||
* Deregisters a hotplug callback.
|
||||
*
|
||||
* Deregister a callback from a libusb_context. This function is safe to call from within
|
||||
* a hotplug callback.
|
||||
*
|
||||
* \param[in] ctx context this callback is registered with
|
||||
* \param[in] handle the handle of the callback to deregister
|
||||
*/
|
||||
void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
|
||||
libusb_hotplug_callback_handle handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -224,6 +224,11 @@ struct libusb_context {
|
|||
struct list_head open_devs;
|
||||
usbi_mutex_t open_devs_lock;
|
||||
|
||||
/* A list of registered hotplug callbacks */
|
||||
struct list_head hotplug_cbs;
|
||||
usbi_mutex_t hotplug_cbs_lock;
|
||||
int hotplug_pipe[2];
|
||||
|
||||
/* this is a list of in-flight transfer handles, sorted by timeout
|
||||
* expiration. URBs to timeout the soonest are placed at the beginning of
|
||||
* the list, URBs that will time out later are placed after, and urbs with
|
||||
|
@ -290,6 +295,7 @@ struct libusb_device {
|
|||
unsigned long session_data;
|
||||
|
||||
struct libusb_device_descriptor device_descriptor;
|
||||
int attached;
|
||||
|
||||
unsigned char os_priv[0];
|
||||
};
|
||||
|
@ -401,6 +407,9 @@ int usbi_device_cache_descriptor(libusb_device *dev);
|
|||
int usbi_get_config_index_by_value(struct libusb_device *dev,
|
||||
uint8_t bConfigurationValue, int *idx);
|
||||
|
||||
void usbi_connect_device (struct libusb_device *dev);
|
||||
void usbi_disconnect_device (struct libusb_device *dev);
|
||||
|
||||
/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */
|
||||
#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD)
|
||||
#include <unistd.h>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#define LIBUSB_MINOR 0
|
||||
#endif
|
||||
#ifndef LIBUSB_MICRO
|
||||
#define LIBUSB_MICRO 15
|
||||
#define LIBUSB_MICRO 16
|
||||
#endif
|
||||
#ifndef LIBUSB_NANO
|
||||
#define LIBUSB_NANO 0
|
||||
|
|
|
@ -1 +1 @@
|
|||
#define LIBUSB_NANO 10651
|
||||
#define LIBUSB_NANO 10652
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue