2 * This is free and unencumbered software released into the public domain.
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
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.
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.
25 * For more information, please refer to <http://unlicense.org/>
28 #define _BSD_SOURCE /* for endian.h */
37 #include <sys/ioctl.h>
39 #include <sys/types.h>
43 #include <sys/eventfd.h>
46 #define IOCB_FLAG_RESFD (1 << 0)
48 #include <linux/usb/functionfs.h>
52 /******************** Descriptors and Strings *******************************/
55 struct usb_functionfs_descs_head_v2 header;
59 struct usb_interface_descriptor intf;
60 struct usb_endpoint_descriptor_no_audio bulk_sink;
61 struct usb_endpoint_descriptor_no_audio bulk_source;
62 } __attribute__ ((__packed__)) fs_descs, hs_descs;
63 } __attribute__ ((__packed__)) descriptors = {
65 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
66 .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
67 FUNCTIONFS_HAS_HS_DESC),
68 .length = htole32(sizeof(descriptors)),
70 .fs_count = htole32(3),
73 .bLength = sizeof(descriptors.fs_descs.intf),
74 .bDescriptorType = USB_DT_INTERFACE,
76 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
80 .bLength = sizeof(descriptors.fs_descs.bulk_sink),
81 .bDescriptorType = USB_DT_ENDPOINT,
82 .bEndpointAddress = 1 | USB_DIR_IN,
83 .bmAttributes = USB_ENDPOINT_XFER_BULK,
86 .bLength = sizeof(descriptors.fs_descs.bulk_source),
87 .bDescriptorType = USB_DT_ENDPOINT,
88 .bEndpointAddress = 2 | USB_DIR_OUT,
89 .bmAttributes = USB_ENDPOINT_XFER_BULK,
92 .hs_count = htole32(3),
95 .bLength = sizeof(descriptors.hs_descs.intf),
96 .bDescriptorType = USB_DT_INTERFACE,
98 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
102 .bLength = sizeof(descriptors.hs_descs.bulk_sink),
103 .bDescriptorType = USB_DT_ENDPOINT,
104 .bEndpointAddress = 1 | USB_DIR_IN,
105 .bmAttributes = USB_ENDPOINT_XFER_BULK,
106 .wMaxPacketSize = htole16(512),
109 .bLength = sizeof(descriptors.hs_descs.bulk_source),
110 .bDescriptorType = USB_DT_ENDPOINT,
111 .bEndpointAddress = 2 | USB_DIR_OUT,
112 .bmAttributes = USB_ENDPOINT_XFER_BULK,
113 .wMaxPacketSize = htole16(512),
118 #define STR_INTERFACE "AIO Test"
120 static const struct {
121 struct usb_functionfs_strings_head header;
124 const char str1[sizeof(STR_INTERFACE)];
125 } __attribute__ ((__packed__)) lang0;
126 } __attribute__ ((__packed__)) strings = {
128 .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
129 .length = htole32(sizeof(strings)),
130 .str_count = htole32(1),
131 .lang_count = htole32(1),
134 htole16(0x0409), /* en-us */
139 /******************** Endpoints handling *******************************/
141 static void display_event(struct usb_functionfs_event *event)
143 static const char *const names[] = {
144 [FUNCTIONFS_BIND] = "BIND",
145 [FUNCTIONFS_UNBIND] = "UNBIND",
146 [FUNCTIONFS_ENABLE] = "ENABLE",
147 [FUNCTIONFS_DISABLE] = "DISABLE",
148 [FUNCTIONFS_SETUP] = "SETUP",
149 [FUNCTIONFS_SUSPEND] = "SUSPEND",
150 [FUNCTIONFS_RESUME] = "RESUME",
152 switch (event->type) {
153 case FUNCTIONFS_BIND:
154 case FUNCTIONFS_UNBIND:
155 case FUNCTIONFS_ENABLE:
156 case FUNCTIONFS_DISABLE:
157 case FUNCTIONFS_SETUP:
158 case FUNCTIONFS_SUSPEND:
159 case FUNCTIONFS_RESUME:
160 printf("Event %s\n", names[event->type]);
164 static void handle_ep0(int ep0, bool *ready)
166 struct usb_functionfs_event event;
169 struct pollfd pfds[1];
171 pfds[0].events = POLLIN;
173 ret = poll(pfds, 1, 0);
175 if (ret && (pfds[0].revents & POLLIN)) {
176 ret = read(ep0, &event, sizeof(event));
178 perror("unable to read event from ep0");
181 display_event(&event);
182 switch (event.type) {
183 case FUNCTIONFS_SETUP:
184 if (event.u.setup.bRequestType & USB_DIR_IN)
190 case FUNCTIONFS_ENABLE:
194 case FUNCTIONFS_DISABLE:
204 int main(int argc, char *argv[])
217 char *buf_in, *buf_out;
218 struct iocb *iocb_in, *iocb_out;
219 int req_in = 0, req_out = 0;
223 printf("ffs directory not specified!\n");
227 ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
233 /* open endpoint files */
234 sprintf(ep_path, "%s/ep0", argv[1]);
235 ep0 = open(ep_path, O_RDWR);
237 perror("unable to open ep0");
240 if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
241 perror("unable do write descriptors");
244 if (write(ep0, &strings, sizeof(strings)) < 0) {
245 perror("unable to write strings");
248 for (i = 0; i < 2; ++i) {
249 sprintf(ep_path, "%s/ep%d", argv[1], i+1);
250 ep[i] = open(ep_path, O_RDWR);
252 printf("unable to open ep%d: %s\n", i+1,
260 memset(&ctx, 0, sizeof(ctx));
261 /* setup aio context to handle up to 2 requests */
262 if (io_setup(2, &ctx) < 0) {
263 perror("unable to setup aio");
267 evfd = eventfd(0, 0);
269 perror("unable to open eventfd");
273 /* alloc buffers and requests */
274 buf_in = malloc(BUF_LEN);
275 buf_out = malloc(BUF_LEN);
276 iocb_in = malloc(sizeof(*iocb_in));
277 iocb_out = malloc(sizeof(*iocb_out));
284 ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
285 &rfds, NULL, NULL, NULL);
293 if (FD_ISSET(ep0, &rfds))
294 handle_ep0(ep0, &ready);
296 /* we are waiting for function ENABLE */
300 /* if something was submitted we wait for event */
301 if (FD_ISSET(evfd, &rfds)) {
303 ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
305 perror("unable to read eventfd");
309 struct io_event e[2];
310 /* we wait for one event */
311 ret = io_getevents(ctx, 1, 2, e, NULL);
312 /* if we got event */
313 for (i = 0; i < ret; ++i) {
314 if (e[i].obj->aio_fildes == ep[0]) {
315 printf("ev=in; ret=%lu\n", e[i].res);
317 } else if (e[i].obj->aio_fildes == ep[1]) {
318 printf("ev=out; ret=%lu\n", e[i].res);
324 if (!req_in) { /* if IN transfer not requested*/
325 /* prepare write request */
326 io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
327 /* enable eventfd notification */
328 iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
329 iocb_in->u.c.resfd = evfd;
330 /* submit table of requests */
331 ret = io_submit(ctx, 1, &iocb_in);
332 if (ret >= 0) { /* if ret > 0 request is queued */
334 printf("submit: in\n");
336 perror("unable to submit request");
338 if (!req_out) { /* if OUT transfer not requested */
339 /* prepare read request */
340 io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
341 /* enable eventfs notification */
342 iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
343 iocb_out->u.c.resfd = evfd;
344 /* submit table of requests */
345 ret = io_submit(ctx, 1, &iocb_out);
346 if (ret >= 0) { /* if ret > 0 request is queued */
348 printf("submit: out\n");
350 perror("unable to submit request");
363 for (i = 0; i < 2; ++i)