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