]> git.sesse.net Git - nageru/blob - patches/libusb-add-support-for-persistent-device-memory.diff
Add some useful (pending) upstream patches that have not gone in yet.
[nageru] / patches / libusb-add-support-for-persistent-device-memory.diff
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
5 \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
12 \r
13 This mirrors new functionality going into Linux' USB stack (recently\r
14 reviewed and acked upstream); only Linux is supported as a backend\r
15 currently.\r
16 ---\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
23 \r
24 diff --git a/libusb/core.c b/libusb/core.c\r
25 index 9c617fb..3eba85a 100644\r
26 --- a/libusb/core.c\r
27 +++ b/libusb/core.c\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
39  }\r
40  \r
41 +API_EXPORTED unsigned char *libusb_dev_mem_alloc(libusb_device_handle *dev,\r
42 +        int length)\r
43 +{\r
44 +       if (!dev->dev->attached)\r
45 +               return NULL;\r
46 +\r
47 +       if (usbi_backend->dev_mem_alloc)\r
48 +               return usbi_backend->dev_mem_alloc(dev, length);\r
49 +       else\r
50 +               return NULL;\r
51 +}\r
52 +\r
53 +/* Note: No current backends actually use the "dev" parameter; it is only there\r
54 + * for any future, less lenient OSes.\r
55 + */\r
56 +int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev,\r
57 +       unsigned char *buffer, int len)\r
58 +{\r
59 +       if (!dev->dev->attached)\r
60 +               return LIBUSB_ERROR_NO_DEVICE;\r
61 +\r
62 +       if (usbi_backend->dev_mem_free)\r
63 +               return usbi_backend->dev_mem_free(dev, buffer, len);\r
64 +       else\r
65 +               return LIBUSB_ERROR_NOT_SUPPORTED;\r
66 +}\r
67 +\r
68  /** \ingroup dev\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
83    libusb_error_name\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
93   */\r
94 -#define LIBUSB_API_VERSION 0x01000104\r
95 +#define LIBUSB_API_VERSION 0x01000105\r
96  \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
102  \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
110 + *\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
114 + *\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
118 + *\r
119 + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105\r
120 + *\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
124 + */\r
125 +unsigned char *LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev,\r
126 +       int length);\r
127 +\r
128 +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev,\r
129 +       unsigned char *buffer, int length);\r
130 +\r
131  /** \ingroup desc\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
141  \r
142 +       /* Allocate persistent DMA memory for the given device, suitable for\r
143 +        * zerocopy. May return NULL on failure. Optional to implement.\r
144 +        */\r
145 +       unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle,\r
146 +               size_t len);\r
147 +\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
151 +\r
152         /* Determine if a kernel driver is active on an interface. Optional.\r
153          *\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
159 @@ -33,6 +33,7 @@\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
169  }\r
170  \r
171 +static unsigned char *op_dev_mem_alloc(struct libusb_device_handle *handle,\r
172 +       size_t len)\r
173 +{\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
179 +                       errno);\r
180 +               return NULL;\r
181 +       }\r
182 +       return buffer;\r
183 +}\r
184 +\r
185 +static int op_dev_mem_free(struct libusb_device_handle *handle,\r
186 +       unsigned char *buffer, size_t len)\r
187 +{\r
188 +       if (munmap(buffer, len) != 0) {\r
189 +               usbi_err(HANDLE_CTX(handle), "free dev mem failed errno %d",\r
190 +                       errno);\r
191 +               return LIBUSB_ERROR_OTHER;\r
192 +       } else {\r
193 +               return LIBUSB_SUCCESS;\r
194 +       }\r
195 +}\r
196 +\r
197  static int op_kernel_driver_active(struct libusb_device_handle *handle,\r
198         int interface)\r
199  {\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
203  \r
204 +       .dev_mem_alloc = op_dev_mem_alloc,\r
205 +       .dev_mem_free = op_dev_mem_free,\r
206 +\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
210 -- \r
211 2.6.4\r
212 \r