summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteinar H. Gunderson <sesse@google.com>2016-02-20 12:26:12 +0100
committerChris Dickens <christopher.a.dickens@gmail.com>2016-05-29 18:51:04 -0700
commita283c3b5a3dce8f6f33331b9aa1d95d41c8f241c (patch)
tree8251d7c0936e41ef31dfbd1b92ea66b12beb93cb
parent0b947e5f9b2dfffa4def7007f4fb5b23fe2eb05f (diff)
downloadlibusb-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>
-rw-r--r--.amend0
-rw-r--r--libusb/core.c56
-rw-r--r--libusb/libusb-1.0.def4
-rw-r--r--libusb/libusb.h10
-rw-r--r--libusb/libusbi.h10
-rw-r--r--libusb/os/linux_usbfs.c30
-rw-r--r--libusb/version_nano.h2
7 files changed, 110 insertions, 2 deletions
diff --git a/.amend b/.amend
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.amend
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