diff options
author | Steinar H. Gunderson <sesse@google.com> | 2016-02-20 12:26:12 +0100 |
---|---|---|
committer | Chris Dickens <christopher.a.dickens@gmail.com> | 2016-05-29 18:51:04 -0700 |
commit | a283c3b5a3dce8f6f33331b9aa1d95d41c8f241c (patch) | |
tree | 8251d7c0936e41ef31dfbd1b92ea66b12beb93cb /libusb | |
parent | 0b947e5f9b2dfffa4def7007f4fb5b23fe2eb05f (diff) | |
download | libusb-a283c3b5a3dce8f6f33331b9aa1d95d41c8f241c.tar.gz |
Add support for persistent device memory.
Add a function to allocate memory belonging to a specific device,
so that the operating system can DMA straight into it for zerocopy,
and also avoid some clearing. Also, this allows up-front memory
allocation in the kernel at program startup; memory allocation is
otherwise done per-transfer, which can fail in a system where memory has become
fragmented over time).
This mirrors new functionality going into Linux' USB stack (recently
reviewed and acked upstream); only Linux is supported as a backend
currently.
[Chris Dickens] Modified to fix doxygen documentation, correct parameter
naming, reposition function declarations, and address a missing request
during the patch review process.
Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
Diffstat (limited to 'libusb')
-rw-r--r-- | libusb/core.c | 56 | ||||
-rw-r--r-- | libusb/libusb-1.0.def | 4 | ||||
-rw-r--r-- | libusb/libusb.h | 10 | ||||
-rw-r--r-- | libusb/libusbi.h | 10 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 30 | ||||
-rw-r--r-- | libusb/version_nano.h | 2 |
6 files changed, 110 insertions, 2 deletions
diff --git a/libusb/core.c b/libusb/core.c index 5317d26..d57edad 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -357,6 +357,8 @@ if (cfg != desired) * - libusb_control_transfer_get_setup() * - libusb_cpu_to_le16() * - libusb_detach_kernel_driver() + * - libusb_dev_mem_alloc() + * - libusb_dev_mem_free() * - libusb_error_name() * - libusb_event_handler_active() * - libusb_event_handling_ok() @@ -1812,6 +1814,60 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, return LIBUSB_ERROR_NOT_SUPPORTED; } +/** \ingroup libusb_asyncio + * Attempts to allocate a block of persistent DMA memory suitable for transfers + * against the given device. If successful, will return a block of memory + * that is suitable for use as "buffer" in \ref libusb_transfer against this + * device. Using this memory instead of regular memory means that the host + * controller can use DMA directly into the buffer to increase performance, and + * also that transfers can no longer fail due to kernel memory fragmentation. + * + * Note that this means you should not modify this memory (or even data on + * the same cache lines) when a transfer is in progress, although it is legal + * to have several transfers going on within the same memory block. + * + * Will return NULL on failure. Many systems do not support such zerocopy + * and will always return NULL. Memory allocated with this function must be + * freed with \ref libusb_dev_mem_free. Specifically, this means that the + * flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated + * with this function. + * + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105 + * + * \param dev_handle a device handle + * \param length size of desired data buffer + * \returns a pointer to the newly allocated memory, or NULL on failure + */ +DEFAULT_VISIBILITY +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length) +{ + if (!dev_handle->dev->attached) + return NULL; + + if (usbi_backend->dev_mem_alloc) + return usbi_backend->dev_mem_alloc(dev_handle, length); + else + return NULL; +} + +/** \ingroup libusb_asyncio + * Free device memory allocated with libusb_dev_mem_alloc(). + * + * \param dev_handle a device handle + * \param buffer pointer to the previously allocated memory + * \param length size of previously allocated memory + * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length) +{ + if (usbi_backend->dev_mem_free) + return usbi_backend->dev_mem_free(dev_handle, buffer, length); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + /** \ingroup libusb_dev * Determine if a kernel driver is active on an interface. If a kernel driver * is active, you cannot claim the interface, and libusb will be unable to diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def index 7b14e0f..2443d9b 100644 --- a/libusb/libusb-1.0.def +++ b/libusb/libusb-1.0.def @@ -20,6 +20,10 @@ EXPORTS libusb_control_transfer@32 = libusb_control_transfer libusb_detach_kernel_driver libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver + libusb_dev_mem_alloc + libusb_dev_mem_alloc@8 = libusb_dev_mem_alloc + libusb_dev_mem_free + libusb_dev_mem_free@12 = libusb_dev_mem_free libusb_error_name libusb_error_name@4 = libusb_error_name libusb_event_handler_active diff --git a/libusb/libusb.h b/libusb/libusb.h index 5b0d522..f73e31c 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -1137,7 +1137,10 @@ enum libusb_transfer_flags { /** Report short frames as errors */ LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, - /** Automatically free() transfer buffer during libusb_free_transfer() */ + /** Automatically free() transfer buffer during libusb_free_transfer(). + * Note that buffers allocated with libusb_dev_mem_alloc() should not + * be attempted freed in this way, since free() is not an appropriate + * way to release such memory. */ LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, /** Automatically call libusb_free_transfer() after callback returns. @@ -1392,6 +1395,11 @@ int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle, int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints); +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length); +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length); + int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle, int interface_number); int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle, diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 75a5a81..b5530d6 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -933,6 +933,16 @@ struct usbi_os_backend { int (*free_streams)(struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints); + /* Allocate persistent DMA memory for the given device, suitable for + * zerocopy. May return NULL on failure. Optional to implement. + */ + unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle, + size_t len); + + /* Free memory allocated by dev_mem_alloc. */ + int (*dev_mem_free)(struct libusb_device_handle *handle, + unsigned char *buffer, size_t len); + /* Determine if a kernel driver is active on an interface. Optional. * * The presence of a kernel driver on an interface indicates that any diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index d154aa9..0689894 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> +#include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/utsname.h> @@ -1557,6 +1558,32 @@ static int op_free_streams(struct libusb_device_handle *handle, endpoints, num_endpoints); } +static unsigned char *op_dev_mem_alloc(struct libusb_device_handle *handle, + size_t len) +{ + struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); + unsigned char *buffer = (unsigned char *)mmap(NULL, len, + PROT_READ | PROT_WRITE, MAP_SHARED, hpriv->fd, 0); + if (buffer == MAP_FAILED) { + usbi_err(HANDLE_CTX(handle), "alloc dev mem failed errno %d", + errno); + return NULL; + } + return buffer; +} + +static int op_dev_mem_free(struct libusb_device_handle *handle, + unsigned char *buffer, size_t len) +{ + if (munmap(buffer, len) != 0) { + usbi_err(HANDLE_CTX(handle), "free dev mem failed errno %d", + errno); + return LIBUSB_ERROR_OTHER; + } else { + return LIBUSB_SUCCESS; + } +} + static int op_kernel_driver_active(struct libusb_device_handle *handle, int interface) { @@ -2678,6 +2705,9 @@ const struct usbi_os_backend linux_usbfs_backend = { .alloc_streams = op_alloc_streams, .free_streams = op_free_streams, + .dev_mem_alloc = op_dev_mem_alloc, + .dev_mem_free = op_dev_mem_free, + .kernel_driver_active = op_kernel_driver_active, .detach_kernel_driver = op_detach_kernel_driver, .attach_kernel_driver = op_attach_kernel_driver, diff --git a/libusb/version_nano.h b/libusb/version_nano.h index b92475f..5921d74 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11109 +#define LIBUSB_NANO 11110 |