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 header;
58 __le32 fs_count;
59 __le32 hs_count;
60 __le32 ss_count;
61 __le32 os_count;
62 struct {
63 struct usb_interface_descriptor intf;
64 struct usb_endpoint_descriptor_no_audio bulk_sink;
65 struct usb_endpoint_descriptor_no_audio bulk_source;
66 } __attribute__ ((__packed__)) fs_descs, hs_descs;
67 struct {
68 struct usb_interface_descriptor intf;
69 struct usb_endpoint_descriptor_no_audio sink;
70 struct usb_ss_ep_comp_descriptor sink_comp;
71 struct usb_endpoint_descriptor_no_audio source;
72 struct usb_ss_ep_comp_descriptor source_comp;
73 } __attribute__ ((__packed__)) ss_descs;
74 struct usb_os_desc_header os_header;
75 struct usb_ext_compat_desc os_desc;
76
77 } __attribute__ ((__packed__)) descriptors = {
78 .header = {
79 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
80 .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
81 FUNCTIONFS_HAS_HS_DESC |
82 FUNCTIONFS_HAS_SS_DESC |
83 FUNCTIONFS_HAS_MS_OS_DESC),
84 .length = htole32(sizeof(descriptors)),
85 },
86 .fs_count = htole32(3),
87 .fs_descs = {
88 .intf = {
89 .bLength = sizeof(descriptors.fs_descs.intf),
90 .bDescriptorType = USB_DT_INTERFACE,
91 .bNumEndpoints = 2,
92 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
93 .iInterface = 1,
94 },
95 .bulk_sink = {
96 .bLength = sizeof(descriptors.fs_descs.bulk_sink),
97 .bDescriptorType = USB_DT_ENDPOINT,
98 .bEndpointAddress = 1 | USB_DIR_IN,
99 .bmAttributes = USB_ENDPOINT_XFER_BULK,
100 },
101 .bulk_source = {
102 .bLength = sizeof(descriptors.fs_descs.bulk_source),
103 .bDescriptorType = USB_DT_ENDPOINT,
104 .bEndpointAddress = 2 | USB_DIR_OUT,
105 .bmAttributes = USB_ENDPOINT_XFER_BULK,
106 },
107 },
108 .hs_count = htole32(3),
109 .hs_descs = {
110 .intf = {
111 .bLength = sizeof(descriptors.hs_descs.intf),
112 .bDescriptorType = USB_DT_INTERFACE,
113 .bNumEndpoints = 2,
114 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
115 .iInterface = 1,
116 },
117 .bulk_sink = {
118 .bLength = sizeof(descriptors.hs_descs.bulk_sink),
119 .bDescriptorType = USB_DT_ENDPOINT,
120 .bEndpointAddress = 1 | USB_DIR_IN,
121 .bmAttributes = USB_ENDPOINT_XFER_BULK,
122 .wMaxPacketSize = htole16(512),
123 },
124 .bulk_source = {
125 .bLength = sizeof(descriptors.hs_descs.bulk_source),
126 .bDescriptorType = USB_DT_ENDPOINT,
127 .bEndpointAddress = 2 | USB_DIR_OUT,
128 .bmAttributes = USB_ENDPOINT_XFER_BULK,
129 .wMaxPacketSize = htole16(512),
130 },
131 },
132 .ss_count = htole32(5),
133 .ss_descs = {
134 .intf = {
135 .bLength = sizeof(descriptors.ss_descs.intf),
136 .bDescriptorType = USB_DT_INTERFACE,
137 .bInterfaceNumber = 0,
138 .bNumEndpoints = 2,
139 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
140 .iInterface = 1,
141 },
142 .sink = {
143 .bLength = sizeof(descriptors.ss_descs.sink),
144 .bDescriptorType = USB_DT_ENDPOINT,
145 .bEndpointAddress = 1 | USB_DIR_IN,
146 .bmAttributes = USB_ENDPOINT_XFER_BULK,
147 .wMaxPacketSize = htole16(1024),
148 },
149 .sink_comp = {
150 .bLength = sizeof(descriptors.ss_descs.sink_comp),
151 .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
152 .bMaxBurst = 4,
153 },
154 .source = {
155 .bLength = sizeof(descriptors.ss_descs.source),
156 .bDescriptorType = USB_DT_ENDPOINT,
157 .bEndpointAddress = 2 | USB_DIR_OUT,
158 .bmAttributes = USB_ENDPOINT_XFER_BULK,
159 .wMaxPacketSize = htole16(1024),
160 },
161 .source_comp = {
162 .bLength = sizeof(descriptors.ss_descs.source_comp),
163 .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
164 .bMaxBurst = 4,
165 },
166 },
167 .os_count = htole32(1),
168 .os_header = {
169 .interface = htole32(1),
170 .dwLength = htole32(sizeof(descriptors.os_header) +
171 sizeof(descriptors.os_desc)),
172 .bcdVersion = htole32(1),
173 .wIndex = htole32(4),
174 .bCount = htole32(1),
175 .Reserved = htole32(0),
176 },
177 .os_desc = {
178 .bFirstInterfaceNumber = 0,
179 .Reserved1 = htole32(1),
180 .CompatibleID = {0},
181 .SubCompatibleID = {0},
182 .Reserved2 = {0},
183 },
184 };
185
186 #define STR_INTERFACE "AIO Test"
187
188 static const struct {
189 struct usb_functionfs_strings_head header;
190 struct {
191 __le16 code;
192 const char str1[sizeof(STR_INTERFACE)];
193 } __attribute__ ((__packed__)) lang0;
194 } __attribute__ ((__packed__)) strings = {
195 .header = {
196 .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
197 .length = htole32(sizeof(strings)),
198 .str_count = htole32(1),
199 .lang_count = htole32(1),
200 },
201 .lang0 = {
202 htole16(0x0409), /* en-us */
203 STR_INTERFACE,
204 },
205 };
206
207 /********************** Buffer structure *******************************/
208
209 struct io_buffer {
210 struct iocb **iocb;
211 unsigned char **buf;
212 unsigned cnt;
213 unsigned len;
214 unsigned requested;
215 };
216
217 /******************** Endpoints handling *******************************/
218
display_event(struct usb_functionfs_event * event)219 static void display_event(struct usb_functionfs_event *event)
220 {
221 static const char *const names[] = {
222 [FUNCTIONFS_BIND] = "BIND",
223 [FUNCTIONFS_UNBIND] = "UNBIND",
224 [FUNCTIONFS_ENABLE] = "ENABLE",
225 [FUNCTIONFS_DISABLE] = "DISABLE",
226 [FUNCTIONFS_SETUP] = "SETUP",
227 [FUNCTIONFS_SUSPEND] = "SUSPEND",
228 [FUNCTIONFS_RESUME] = "RESUME",
229 };
230 switch (event->type) {
231 case FUNCTIONFS_BIND:
232 case FUNCTIONFS_UNBIND:
233 case FUNCTIONFS_ENABLE:
234 case FUNCTIONFS_DISABLE:
235 case FUNCTIONFS_SETUP:
236 case FUNCTIONFS_SUSPEND:
237 case FUNCTIONFS_RESUME:
238 printf("Event %s\n", names[event->type]);
239 }
240 }
241
handle_ep0(int ep0,bool * ready)242 static void handle_ep0(int ep0, bool *ready)
243 {
244 int ret;
245 struct usb_functionfs_event event;
246
247 ret = read(ep0, &event, sizeof(event));
248 if (!ret) {
249 perror("unable to read event from ep0");
250 return;
251 }
252 display_event(&event);
253 switch (event.type) {
254 case FUNCTIONFS_SETUP:
255 if (event.u.setup.bRequestType & USB_DIR_IN)
256 write(ep0, NULL, 0);
257 else
258 read(ep0, NULL, 0);
259 break;
260
261 case FUNCTIONFS_ENABLE:
262 *ready = true;
263 break;
264
265 case FUNCTIONFS_DISABLE:
266 *ready = false;
267 break;
268
269 default:
270 break;
271 }
272 }
273
init_bufs(struct io_buffer * iobuf,unsigned n,unsigned len)274 void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)
275 {
276 unsigned i;
277 iobuf->buf = malloc(n*sizeof(*iobuf->buf));
278 iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
279 iobuf->cnt = n;
280 iobuf->len = len;
281 iobuf->requested = 0;
282 for (i = 0; i < n; ++i) {
283 iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
284 iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
285 }
286 iobuf->cnt = n;
287 }
288
delete_bufs(struct io_buffer * iobuf)289 void delete_bufs(struct io_buffer *iobuf)
290 {
291 unsigned i;
292 for (i = 0; i < iobuf->cnt; ++i) {
293 free(iobuf->buf[i]);
294 free(iobuf->iocb[i]);
295 }
296 free(iobuf->buf);
297 free(iobuf->iocb);
298 }
299
main(int argc,char * argv[])300 int main(int argc, char *argv[])
301 {
302 int ret;
303 unsigned i, j;
304 char *ep_path;
305
306 int ep0, ep1;
307
308 io_context_t ctx;
309
310 int evfd;
311 fd_set rfds;
312
313 struct io_buffer iobuf[2];
314 int actual = 0;
315 bool ready;
316
317 if (argc != 2) {
318 printf("ffs directory not specified!\n");
319 return 1;
320 }
321
322 ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
323 if (!ep_path) {
324 perror("malloc");
325 return 1;
326 }
327
328 /* open endpoint files */
329 sprintf(ep_path, "%s/ep0", argv[1]);
330 ep0 = open(ep_path, O_RDWR);
331 if (ep0 < 0) {
332 perror("unable to open ep0");
333 return 1;
334 }
335 if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
336 perror("unable do write descriptors");
337 return 1;
338 }
339 if (write(ep0, &strings, sizeof(strings)) < 0) {
340 perror("unable to write strings");
341 return 1;
342 }
343 sprintf(ep_path, "%s/ep1", argv[1]);
344 ep1 = open(ep_path, O_RDWR);
345 if (ep1 < 0) {
346 perror("unable to open ep1");
347 return 1;
348 }
349
350 free(ep_path);
351
352 memset(&ctx, 0, sizeof(ctx));
353 /* setup aio context to handle up to AIO_MAX requests */
354 if (io_setup(AIO_MAX, &ctx) < 0) {
355 perror("unable to setup aio");
356 return 1;
357 }
358
359 evfd = eventfd(0, 0);
360 if (evfd < 0) {
361 perror("unable to open eventfd");
362 return 1;
363 }
364
365 for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
366 init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);
367
368 while (1) {
369 FD_ZERO(&rfds);
370 FD_SET(ep0, &rfds);
371 FD_SET(evfd, &rfds);
372
373 ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
374 &rfds, NULL, NULL, NULL);
375 if (ret < 0) {
376 if (errno == EINTR)
377 continue;
378 perror("select");
379 break;
380 }
381
382 if (FD_ISSET(ep0, &rfds))
383 handle_ep0(ep0, &ready);
384
385 /* we are waiting for function ENABLE */
386 if (!ready)
387 continue;
388
389 /*
390 * when we're preparing new data to submit,
391 * second buffer being transmitted
392 */
393 for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {
394 if (iobuf[i].requested)
395 continue;
396 /* prepare requests */
397 for (j = 0; j < iobuf[i].cnt; ++j) {
398 io_prep_pwrite(iobuf[i].iocb[j], ep1,
399 iobuf[i].buf[j],
400 iobuf[i].len, 0);
401 /* enable eventfd notification */
402 iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;
403 iobuf[i].iocb[j]->u.c.resfd = evfd;
404 }
405 /* submit table of requests */
406 ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);
407 if (ret >= 0) {
408 iobuf[i].requested = ret;
409 printf("submit: %d requests buf: %d\n", ret, i);
410 } else
411 perror("unable to submit requests");
412 }
413
414 /* if event is ready to read */
415 if (!FD_ISSET(evfd, &rfds))
416 continue;
417
418 uint64_t ev_cnt;
419 ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
420 if (ret < 0) {
421 perror("unable to read eventfd");
422 break;
423 }
424
425 struct io_event e[BUFS_MAX];
426 /* we read aio events */
427 ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);
428 if (ret > 0) /* if we got events */
429 iobuf[actual].requested -= ret;
430
431 /* if all req's from iocb completed */
432 if (!iobuf[actual].requested)
433 actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));
434 }
435
436 /* free resources */
437
438 for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
439 delete_bufs(&iobuf[i]);
440 io_destroy(ctx);
441
442 close(ep1);
443 close(ep0);
444
445 return 0;
446 }
447