1 | /* |
2 | * This is free and unencumbered software released into the public domain. |
3 | * |
4 | * Anyone is free to copy, modify, publish, use, compile, sell, or |
5 | * distribute this software, either in source code form or as a compiled |
6 | * binary, for any purpose, commercial or non-commercial, and by any |
7 | * means. |
8 | * |
9 | * In jurisdictions that recognize copyright laws, the author or authors |
10 | * of this software dedicate any and all copyright interest in the |
11 | * software to the public domain. We make this dedication for the benefit |
12 | * of the public at large and to the detriment of our heirs and |
13 | * successors. We intend this dedication to be an overt act of |
14 | * relinquishment in perpetuity of all present and future rights to this |
15 | * software under copyright law. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
23 | * OTHER DEALINGS IN THE SOFTWARE. |
24 | * |
25 | * For more information, please refer to <http://unlicense.org/> |
26 | */ |
27 | |
28 | #define _BSD_SOURCE /* for endian.h */ |
29 | |
30 | #include <endian.h> |
31 | #include <errno.h> |
32 | #include <fcntl.h> |
33 | #include <stdarg.h> |
34 | #include <stdio.h> |
35 | #include <stdlib.h> |
36 | #include <string.h> |
37 | #include <sys/ioctl.h> |
38 | #include <sys/stat.h> |
39 | #include <sys/types.h> |
40 | #include <sys/poll.h> |
41 | #include <unistd.h> |
42 | #include <stdbool.h> |
43 | #include <sys/eventfd.h> |
44 | |
45 | #include "libaio.h" |
46 | #define IOCB_FLAG_RESFD (1 << 0) |
47 | |
48 | #include <linux/usb/functionfs.h> |
49 | |
50 | #define BUF_LEN 8192 |
51 | #define BUFS_MAX 128 |
52 | #define AIO_MAX (BUFS_MAX*2) |
53 | |
54 | /******************** Descriptors and Strings *******************************/ |
55 | |
56 | static const struct { |
57 | struct usb_functionfs_descs_head_v2 ; |
58 | __le32 fs_count; |
59 | __le32 hs_count; |
60 | struct { |
61 | struct usb_interface_descriptor intf; |
62 | struct usb_endpoint_descriptor_no_audio bulk_sink; |
63 | struct usb_endpoint_descriptor_no_audio bulk_source; |
64 | } __attribute__ ((__packed__)) fs_descs, hs_descs; |
65 | } __attribute__ ((__packed__)) descriptors = { |
66 | .header = { |
67 | .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), |
68 | .flags = htole32(FUNCTIONFS_HAS_FS_DESC | |
69 | FUNCTIONFS_HAS_HS_DESC), |
70 | .length = htole32(sizeof(descriptors)), |
71 | }, |
72 | .fs_count = htole32(3), |
73 | .fs_descs = { |
74 | .intf = { |
75 | .bLength = sizeof(descriptors.fs_descs.intf), |
76 | .bDescriptorType = USB_DT_INTERFACE, |
77 | .bNumEndpoints = 2, |
78 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, |
79 | .iInterface = 1, |
80 | }, |
81 | .bulk_sink = { |
82 | .bLength = sizeof(descriptors.fs_descs.bulk_sink), |
83 | .bDescriptorType = USB_DT_ENDPOINT, |
84 | .bEndpointAddress = 1 | USB_DIR_IN, |
85 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
86 | }, |
87 | .bulk_source = { |
88 | .bLength = sizeof(descriptors.fs_descs.bulk_source), |
89 | .bDescriptorType = USB_DT_ENDPOINT, |
90 | .bEndpointAddress = 2 | USB_DIR_OUT, |
91 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
92 | }, |
93 | }, |
94 | .hs_count = htole32(3), |
95 | .hs_descs = { |
96 | .intf = { |
97 | .bLength = sizeof(descriptors.hs_descs.intf), |
98 | .bDescriptorType = USB_DT_INTERFACE, |
99 | .bNumEndpoints = 2, |
100 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, |
101 | .iInterface = 1, |
102 | }, |
103 | .bulk_sink = { |
104 | .bLength = sizeof(descriptors.hs_descs.bulk_sink), |
105 | .bDescriptorType = USB_DT_ENDPOINT, |
106 | .bEndpointAddress = 1 | USB_DIR_IN, |
107 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
108 | .wMaxPacketSize = htole16(512), |
109 | }, |
110 | .bulk_source = { |
111 | .bLength = sizeof(descriptors.hs_descs.bulk_source), |
112 | .bDescriptorType = USB_DT_ENDPOINT, |
113 | .bEndpointAddress = 2 | USB_DIR_OUT, |
114 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
115 | .wMaxPacketSize = htole16(512), |
116 | }, |
117 | }, |
118 | }; |
119 | |
120 | #define STR_INTERFACE "AIO Test" |
121 | |
122 | static const struct { |
123 | struct usb_functionfs_strings_head ; |
124 | struct { |
125 | __le16 code; |
126 | const char str1[sizeof(STR_INTERFACE)]; |
127 | } __attribute__ ((__packed__)) lang0; |
128 | } __attribute__ ((__packed__)) strings = { |
129 | .header = { |
130 | .magic = htole32(FUNCTIONFS_STRINGS_MAGIC), |
131 | .length = htole32(sizeof(strings)), |
132 | .str_count = htole32(1), |
133 | .lang_count = htole32(1), |
134 | }, |
135 | .lang0 = { |
136 | htole16(0x0409), /* en-us */ |
137 | STR_INTERFACE, |
138 | }, |
139 | }; |
140 | |
141 | /********************** Buffer structure *******************************/ |
142 | |
143 | struct io_buffer { |
144 | struct iocb **iocb; |
145 | unsigned char **buf; |
146 | unsigned cnt; |
147 | unsigned len; |
148 | unsigned requested; |
149 | }; |
150 | |
151 | /******************** Endpoints handling *******************************/ |
152 | |
153 | static void display_event(struct usb_functionfs_event *event) |
154 | { |
155 | static const char *const names[] = { |
156 | [FUNCTIONFS_BIND] = "BIND" , |
157 | [FUNCTIONFS_UNBIND] = "UNBIND" , |
158 | [FUNCTIONFS_ENABLE] = "ENABLE" , |
159 | [FUNCTIONFS_DISABLE] = "DISABLE" , |
160 | [FUNCTIONFS_SETUP] = "SETUP" , |
161 | [FUNCTIONFS_SUSPEND] = "SUSPEND" , |
162 | [FUNCTIONFS_RESUME] = "RESUME" , |
163 | }; |
164 | switch (event->type) { |
165 | case FUNCTIONFS_BIND: |
166 | case FUNCTIONFS_UNBIND: |
167 | case FUNCTIONFS_ENABLE: |
168 | case FUNCTIONFS_DISABLE: |
169 | case FUNCTIONFS_SETUP: |
170 | case FUNCTIONFS_SUSPEND: |
171 | case FUNCTIONFS_RESUME: |
172 | printf("Event %s\n" , names[event->type]); |
173 | } |
174 | } |
175 | |
176 | static void handle_ep0(int ep0, bool *ready) |
177 | { |
178 | int ret; |
179 | struct usb_functionfs_event event; |
180 | |
181 | ret = read(ep0, &event, sizeof(event)); |
182 | if (!ret) { |
183 | perror("unable to read event from ep0" ); |
184 | return; |
185 | } |
186 | display_event(event: &event); |
187 | switch (event.type) { |
188 | case FUNCTIONFS_SETUP: |
189 | if (event.u.setup.bRequestType & USB_DIR_IN) |
190 | write(ep0, NULL, 0); |
191 | else |
192 | read(ep0, NULL, 0); |
193 | break; |
194 | |
195 | case FUNCTIONFS_ENABLE: |
196 | *ready = true; |
197 | break; |
198 | |
199 | case FUNCTIONFS_DISABLE: |
200 | *ready = false; |
201 | break; |
202 | |
203 | default: |
204 | break; |
205 | } |
206 | } |
207 | |
208 | void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len) |
209 | { |
210 | unsigned i; |
211 | iobuf->buf = malloc(n*sizeof(*iobuf->buf)); |
212 | iobuf->iocb = malloc(n*sizeof(*iobuf->iocb)); |
213 | iobuf->cnt = n; |
214 | iobuf->len = len; |
215 | iobuf->requested = 0; |
216 | for (i = 0; i < n; ++i) { |
217 | iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf)); |
218 | iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb)); |
219 | } |
220 | iobuf->cnt = n; |
221 | } |
222 | |
223 | void delete_bufs(struct io_buffer *iobuf) |
224 | { |
225 | unsigned i; |
226 | for (i = 0; i < iobuf->cnt; ++i) { |
227 | free(iobuf->buf[i]); |
228 | free(iobuf->iocb[i]); |
229 | } |
230 | free(iobuf->buf); |
231 | free(iobuf->iocb); |
232 | } |
233 | |
234 | int main(int argc, char *argv[]) |
235 | { |
236 | int ret; |
237 | unsigned i, j; |
238 | char *ep_path; |
239 | |
240 | int ep0, ep1; |
241 | |
242 | io_context_t ctx; |
243 | |
244 | int evfd; |
245 | fd_set rfds; |
246 | |
247 | struct io_buffer iobuf[2]; |
248 | int actual = 0; |
249 | bool ready; |
250 | |
251 | if (argc != 2) { |
252 | printf("ffs directory not specified!\n" ); |
253 | return 1; |
254 | } |
255 | |
256 | ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */); |
257 | if (!ep_path) { |
258 | perror("malloc" ); |
259 | return 1; |
260 | } |
261 | |
262 | /* open endpoint files */ |
263 | sprintf(ep_path, "%s/ep0" , argv[1]); |
264 | ep0 = open(ep_path, O_RDWR); |
265 | if (ep0 < 0) { |
266 | perror("unable to open ep0" ); |
267 | return 1; |
268 | } |
269 | if (write(ep0, &descriptors, sizeof(descriptors)) < 0) { |
270 | perror("unable do write descriptors" ); |
271 | return 1; |
272 | } |
273 | if (write(ep0, &strings, sizeof(strings)) < 0) { |
274 | perror("unable to write strings" ); |
275 | return 1; |
276 | } |
277 | sprintf(ep_path, "%s/ep1" , argv[1]); |
278 | ep1 = open(ep_path, O_RDWR); |
279 | if (ep1 < 0) { |
280 | perror("unable to open ep1" ); |
281 | return 1; |
282 | } |
283 | |
284 | free(ep_path); |
285 | |
286 | memset(&ctx, 0, sizeof(ctx)); |
287 | /* setup aio context to handle up to AIO_MAX requests */ |
288 | if (io_setup(AIO_MAX, &ctx) < 0) { |
289 | perror("unable to setup aio" ); |
290 | return 1; |
291 | } |
292 | |
293 | evfd = eventfd(0, 0); |
294 | if (evfd < 0) { |
295 | perror("unable to open eventfd" ); |
296 | return 1; |
297 | } |
298 | |
299 | for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) |
300 | init_bufs(iobuf: &iobuf[i], BUFS_MAX, BUF_LEN); |
301 | |
302 | while (1) { |
303 | FD_ZERO(&rfds); |
304 | FD_SET(ep0, &rfds); |
305 | FD_SET(evfd, &rfds); |
306 | |
307 | ret = select(((ep0 > evfd) ? ep0 : evfd)+1, |
308 | &rfds, NULL, NULL, NULL); |
309 | if (ret < 0) { |
310 | if (errno == EINTR) |
311 | continue; |
312 | perror("select" ); |
313 | break; |
314 | } |
315 | |
316 | if (FD_ISSET(ep0, &rfds)) |
317 | handle_ep0(ep0, ready: &ready); |
318 | |
319 | /* we are waiting for function ENABLE */ |
320 | if (!ready) |
321 | continue; |
322 | |
323 | /* |
324 | * when we're preparing new data to submit, |
325 | * second buffer being transmitted |
326 | */ |
327 | for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) { |
328 | if (iobuf[i].requested) |
329 | continue; |
330 | /* prepare requests */ |
331 | for (j = 0; j < iobuf[i].cnt; ++j) { |
332 | io_prep_pwrite(iobuf[i].iocb[j], ep1, |
333 | iobuf[i].buf[j], |
334 | iobuf[i].len, 0); |
335 | /* enable eventfd notification */ |
336 | iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD; |
337 | iobuf[i].iocb[j]->u.c.resfd = evfd; |
338 | } |
339 | /* submit table of requests */ |
340 | ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb); |
341 | if (ret >= 0) { |
342 | iobuf[i].requested = ret; |
343 | printf("submit: %d requests buf: %d\n" , ret, i); |
344 | } else |
345 | perror("unable to submit requests" ); |
346 | } |
347 | |
348 | /* if event is ready to read */ |
349 | if (!FD_ISSET(evfd, &rfds)) |
350 | continue; |
351 | |
352 | uint64_t ev_cnt; |
353 | ret = read(evfd, &ev_cnt, sizeof(ev_cnt)); |
354 | if (ret < 0) { |
355 | perror("unable to read eventfd" ); |
356 | break; |
357 | } |
358 | |
359 | struct io_event e[BUFS_MAX]; |
360 | /* we read aio events */ |
361 | ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL); |
362 | if (ret > 0) /* if we got events */ |
363 | iobuf[actual].requested -= ret; |
364 | |
365 | /* if all req's from iocb completed */ |
366 | if (!iobuf[actual].requested) |
367 | actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf)); |
368 | } |
369 | |
370 | /* free resources */ |
371 | |
372 | for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) |
373 | delete_bufs(iobuf: &iobuf[i]); |
374 | io_destroy(ctx); |
375 | |
376 | close(ep1); |
377 | close(ep0); |
378 | |
379 | return 0; |
380 | } |
381 | |