1 From 66c3edf744415f117c3be95ae83f0bab6e128830 Mon Sep 17 00:00:00 2001
\r
2 From: "Steinar H. Gunderson" <sesse@google.com>
\r
3 Date: Wed, 9 Dec 2015 10:03:15 +0100
\r
4 Subject: [PATCH] Add support for persistent device memory.
\r
6 Add a function to allocate memory belonging to a specific device,
\r
7 so that the operating system can DMA straight into it for zerocopy,
\r
8 and also avoid some clearing. Also, this allows up-front memory
\r
9 allocation in the kernel at program startup; memory allocation is
\r
10 otherwise done per-transfer, which can fail in a system where memory has become
\r
11 fragmented over time).
\r
13 This mirrors new functionality going into Linux' USB stack (scheduled
\r
14 for inclusion in mainline around 4.6); only Linux is supported as a
\r
17 libusb/core.c | 29 +++++++++++++++++++++++++++++
\r
18 libusb/libusb-1.0.def | 4 ++++
\r
19 libusb/libusb.h | 30 +++++++++++++++++++++++++++++-
\r
20 libusb/libusbi.h | 10 ++++++++++
\r
21 libusb/os/linux_usbfs.c | 30 ++++++++++++++++++++++++++++++
\r
22 5 files changed, 102 insertions(+), 1 deletion(-)
\r
24 diff --git a/libusb/core.c b/libusb/core.c
\r
25 index 9c617fb..3eba85a 100644
\r
28 @@ -351,6 +351,8 @@ if (cfg != desired)
\r
29 * - libusb_control_transfer_get_setup()
\r
30 * - libusb_cpu_to_le16()
\r
31 * - libusb_detach_kernel_driver()
\r
32 + * - libusb_dev_mem_alloc()
\r
33 + * - libusb_dev_mem_free()
\r
34 * - libusb_error_name()
\r
35 * - libusb_event_handler_active()
\r
36 * - libusb_event_handling_ok()
\r
37 @@ -1805,6 +1807,33 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev,
\r
38 return LIBUSB_ERROR_NOT_SUPPORTED;
\r
41 +API_EXPORTED unsigned char *libusb_dev_mem_alloc(libusb_device_handle *dev,
\r
44 + if (!dev->dev->attached)
\r
47 + if (usbi_backend->dev_mem_alloc)
\r
48 + return usbi_backend->dev_mem_alloc(dev, length);
\r
53 +/* Note: No current backends actually use the "dev" parameter; it is only there
\r
54 + * for any future, less lenient OSes.
\r
56 +int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev,
\r
57 + unsigned char *buffer, int len)
\r
59 + if (!dev->dev->attached)
\r
60 + return LIBUSB_ERROR_NO_DEVICE;
\r
62 + if (usbi_backend->dev_mem_free)
\r
63 + return usbi_backend->dev_mem_free(dev, buffer, len);
\r
65 + return LIBUSB_ERROR_NOT_SUPPORTED;
\r
69 * Determine if a kernel driver is active on an interface. If a kernel driver
\r
70 * is active, you cannot claim the interface, and libusb will be unable to
\r
71 diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def
\r
72 index 538ad49..e040f4b 100644
\r
73 --- a/libusb/libusb-1.0.def
\r
74 +++ b/libusb/libusb-1.0.def
\r
75 @@ -20,6 +20,10 @@ EXPORTS
\r
76 libusb_control_transfer@32 = libusb_control_transfer
\r
77 libusb_detach_kernel_driver
\r
78 libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver
\r
79 + libusb_dev_mem_alloc
\r
80 + libusb_dev_mem_alloc@8 = libusb_dev_mem_alloc
\r
81 + libusb_dev_mem_free
\r
82 + libusb_dev_mem_free@12 = libusb_dev_mem_free
\r
84 libusb_error_name@4 = libusb_error_name
\r
85 libusb_event_handler_active
\r
86 diff --git a/libusb/libusb.h b/libusb/libusb.h
\r
87 index 513945f..5a84f5b 100644
\r
88 --- a/libusb/libusb.h
\r
89 +++ b/libusb/libusb.h
\r
90 @@ -141,7 +141,7 @@ typedef unsigned __int32 uint32_t;
\r
91 * Internally, LIBUSB_API_VERSION is defined as follows:
\r
92 * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
\r
94 -#define LIBUSB_API_VERSION 0x01000104
\r
95 +#define LIBUSB_API_VERSION 0x01000105
\r
97 /* The following is kept for compatibility, but will be deprecated in the future */
\r
98 #define LIBUSBX_API_VERSION LIBUSB_API_VERSION
\r
99 @@ -1749,6 +1749,34 @@ int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle,
\r
100 unsigned char endpoint, unsigned char *data, int length,
\r
101 int *actual_length, unsigned int timeout);
\r
103 +/** \ingroup asyncio
\r
104 + * Attempts to allocate a block of persistent DMA memory suitable for transfers
\r
105 + * against the given device. If successful, will return a block of memory
\r
106 + * that is suitable for use as "buffer" in \ref libusb_transfer against this
\r
107 + * device. Using this memory instead of regular memory means that the host
\r
108 + * controller can use DMA directly into the buffer to increase performance, and
\r
109 + * also that transfers can no longer fail due to kernel memory fragmentation.
\r
111 + * Note that this means you should not modify this memory (or even data on
\r
112 + * the same cache lines) when a transfer is in progress, although it is legal
\r
113 + * to have several transfers going on within the same memory block.
\r
115 + * Will return NULL on failure. Many systems do not support such zerocopy
\r
116 + * and will always return NULL. Memory allocated with this function must be
\r
117 + * freed with \ref libusb_dev_mem_free.
\r
119 + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105
\r
121 + * \param dev a device handle
\r
122 + * \param length size of desired data buffer
\r
123 + * \returns a pointer to the newly allocated memory, or NULL on failure
\r
125 +unsigned char *LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev,
\r
128 +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev,
\r
129 + unsigned char *buffer, int length);
\r
132 * Retrieve a descriptor from the default control pipe.
\r
133 * This is a convenience function which formulates the appropriate control
\r
134 diff --git a/libusb/libusbi.h b/libusb/libusbi.h
\r
135 index f1afd99..66bdf46 100644
\r
136 --- a/libusb/libusbi.h
\r
137 +++ b/libusb/libusbi.h
\r
138 @@ -913,6 +913,16 @@ struct usbi_os_backend {
\r
139 int (*free_streams)(struct libusb_device_handle *handle,
\r
140 unsigned char *endpoints, int num_endpoints);
\r
142 + /* Allocate persistent DMA memory for the given device, suitable for
\r
143 + * zerocopy. May return NULL on failure. Optional to implement.
\r
145 + unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle,
\r
148 + /* Free memory allocated by dev_mem_alloc. */
\r
149 + int (*dev_mem_free)(struct libusb_device_handle *handle,
\r
150 + unsigned char *buffer, size_t len);
\r
152 /* Determine if a kernel driver is active on an interface. Optional.
\r
154 * The presence of a kernel driver on an interface indicates that any
\r
155 diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
\r
156 index a63852f..a167084 100644
\r
157 --- a/libusb/os/linux_usbfs.c
\r
158 +++ b/libusb/os/linux_usbfs.c
\r
160 #include <stdlib.h>
\r
161 #include <string.h>
\r
162 #include <sys/ioctl.h>
\r
163 +#include <sys/mman.h>
\r
164 #include <sys/stat.h>
\r
165 #include <sys/types.h>
\r
166 #include <sys/utsname.h>
\r
167 @@ -1561,6 +1562,32 @@ static int op_free_streams(struct libusb_device_handle *handle,
\r
168 endpoints, num_endpoints);
\r
171 +static unsigned char *op_dev_mem_alloc(struct libusb_device_handle *handle,
\r
174 + struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
\r
175 + unsigned char *buffer = (unsigned char *)mmap(NULL, len,
\r
176 + PROT_READ | PROT_WRITE, MAP_SHARED, hpriv->fd, 0);
\r
177 + if (buffer == MAP_FAILED) {
\r
178 + usbi_err(HANDLE_CTX(handle), "alloc dev mem failed errno %d",
\r
185 +static int op_dev_mem_free(struct libusb_device_handle *handle,
\r
186 + unsigned char *buffer, size_t len)
\r
188 + if (munmap(buffer, len) != 0) {
\r
189 + usbi_err(HANDLE_CTX(handle), "free dev mem failed errno %d",
\r
191 + return LIBUSB_ERROR_OTHER;
\r
193 + return LIBUSB_SUCCESS;
\r
197 static int op_kernel_driver_active(struct libusb_device_handle *handle,
\r
200 @@ -2682,6 +2709,9 @@ const struct usbi_os_backend linux_usbfs_backend = {
\r
201 .alloc_streams = op_alloc_streams,
\r
202 .free_streams = op_free_streams,
\r
204 + .dev_mem_alloc = op_dev_mem_alloc,
\r
205 + .dev_mem_free = op_dev_mem_free,
\r
207 .kernel_driver_active = op_kernel_driver_active,
\r
208 .detach_kernel_driver = op_detach_kernel_driver,
\r
209 .attach_kernel_driver = op_attach_kernel_driver,
\r