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 /* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */
29
30 #define _DEFAULT_SOURCE /* for endian.h */
31
32 #include <endian.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/ioctl.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/poll.h>
43 #include <unistd.h>
44 #include <stdbool.h>
45 #include <sys/eventfd.h>
46
47 #include "libaio.h"
48 #define IOCB_FLAG_RESFD (1 << 0)
49
50 #include <linux/usb/functionfs.h>
51
52 #define BUF_LEN 8192
53
54 /*
55 * cpu_to_le16/32 are used when initializing structures, a context where a
56 * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way
57 * that allows them to be used when initializing structures.
58 */
59
60 #if BYTE_ORDER == __LITTLE_ENDIAN
61 #define cpu_to_le16(x) (x)
62 #define cpu_to_le32(x) (x)
63 #else
64 #define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
65 #define cpu_to_le32(x) \
66 ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
67 (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
68 #endif
69
70 /******************** Descriptors and Strings *******************************/
71
72 static const struct {
73 struct usb_functionfs_descs_head_v2 header;
74 __le32 fs_count;
75 __le32 hs_count;
76 struct {
77 struct usb_interface_descriptor intf;
78 struct usb_endpoint_descriptor_no_audio bulk_sink;
79 struct usb_endpoint_descriptor_no_audio bulk_source;
80 } __attribute__ ((__packed__)) fs_descs, hs_descs;
81 } __attribute__ ((__packed__)) descriptors = {
82 .header = {
83 .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
84 .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
85 FUNCTIONFS_HAS_HS_DESC),
86 .length = cpu_to_le32(sizeof(descriptors)),
87 },
88 .fs_count = cpu_to_le32(3),
89 .fs_descs = {
90 .intf = {
91 .bLength = sizeof(descriptors.fs_descs.intf),
92 .bDescriptorType = USB_DT_INTERFACE,
93 .bNumEndpoints = 2,
94 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
95 .iInterface = 1,
96 },
97 .bulk_sink = {
98 .bLength = sizeof(descriptors.fs_descs.bulk_sink),
99 .bDescriptorType = USB_DT_ENDPOINT,
100 .bEndpointAddress = 1 | USB_DIR_IN,
101 .bmAttributes = USB_ENDPOINT_XFER_BULK,
102 },
103 .bulk_source = {
104 .bLength = sizeof(descriptors.fs_descs.bulk_source),
105 .bDescriptorType = USB_DT_ENDPOINT,
106 .bEndpointAddress = 2 | USB_DIR_OUT,
107 .bmAttributes = USB_ENDPOINT_XFER_BULK,
108 },
109 },
110 .hs_count = cpu_to_le32(3),
111 .hs_descs = {
112 .intf = {
113 .bLength = sizeof(descriptors.hs_descs.intf),
114 .bDescriptorType = USB_DT_INTERFACE,
115 .bNumEndpoints = 2,
116 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
117 .iInterface = 1,
118 },
119 .bulk_sink = {
120 .bLength = sizeof(descriptors.hs_descs.bulk_sink),
121 .bDescriptorType = USB_DT_ENDPOINT,
122 .bEndpointAddress = 1 | USB_DIR_IN,
123 .bmAttributes = USB_ENDPOINT_XFER_BULK,
124 .wMaxPacketSize = cpu_to_le16(512),
125 },
126 .bulk_source = {
127 .bLength = sizeof(descriptors.hs_descs.bulk_source),
128 .bDescriptorType = USB_DT_ENDPOINT,
129 .bEndpointAddress = 2 | USB_DIR_OUT,
130 .bmAttributes = USB_ENDPOINT_XFER_BULK,
131 .wMaxPacketSize = cpu_to_le16(512),
132 },
133 },
134 };
135
136 #define STR_INTERFACE "AIO Test"
137
138 static const struct {
139 struct usb_functionfs_strings_head header;
140 struct {
141 __le16 code;
142 const char str1[sizeof(STR_INTERFACE)];
143 } __attribute__ ((__packed__)) lang0;
144 } __attribute__ ((__packed__)) strings = {
145 .header = {
146 .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
147 .length = cpu_to_le32(sizeof(strings)),
148 .str_count = cpu_to_le32(1),
149 .lang_count = cpu_to_le32(1),
150 },
151 .lang0 = {
152 cpu_to_le16(0x0409), /* en-us */
153 STR_INTERFACE,
154 },
155 };
156
157 /******************** Endpoints handling *******************************/
158
display_event(struct usb_functionfs_event * event)159 static void display_event(struct usb_functionfs_event *event)
160 {
161 static const char *const names[] = {
162 [FUNCTIONFS_BIND] = "BIND",
163 [FUNCTIONFS_UNBIND] = "UNBIND",
164 [FUNCTIONFS_ENABLE] = "ENABLE",
165 [FUNCTIONFS_DISABLE] = "DISABLE",
166 [FUNCTIONFS_SETUP] = "SETUP",
167 [FUNCTIONFS_SUSPEND] = "SUSPEND",
168 [FUNCTIONFS_RESUME] = "RESUME",
169 };
170 switch (event->type) {
171 case FUNCTIONFS_BIND:
172 case FUNCTIONFS_UNBIND:
173 case FUNCTIONFS_ENABLE:
174 case FUNCTIONFS_DISABLE:
175 case FUNCTIONFS_SETUP:
176 case FUNCTIONFS_SUSPEND:
177 case FUNCTIONFS_RESUME:
178 printf("Event %s\n", names[event->type]);
179 }
180 }
181
handle_ep0(int ep0,bool * ready)182 static void handle_ep0(int ep0, bool *ready)
183 {
184 struct usb_functionfs_event event;
185 int ret;
186
187 struct pollfd pfds[1];
188 pfds[0].fd = ep0;
189 pfds[0].events = POLLIN;
190
191 ret = poll(pfds, 1, 0);
192
193 if (ret && (pfds[0].revents & POLLIN)) {
194 ret = read(ep0, &event, sizeof(event));
195 if (!ret) {
196 perror("unable to read event from ep0");
197 return;
198 }
199 display_event(&event);
200 switch (event.type) {
201 case FUNCTIONFS_SETUP:
202 if (event.u.setup.bRequestType & USB_DIR_IN)
203 write(ep0, NULL, 0);
204 else
205 read(ep0, NULL, 0);
206 break;
207
208 case FUNCTIONFS_ENABLE:
209 *ready = true;
210 break;
211
212 case FUNCTIONFS_DISABLE:
213 *ready = false;
214 break;
215
216 default:
217 break;
218 }
219 }
220 }
221
main(int argc,char * argv[])222 int main(int argc, char *argv[])
223 {
224 int i, ret;
225 char *ep_path;
226
227 int ep0;
228 int ep[2];
229
230 io_context_t ctx;
231
232 int evfd;
233 fd_set rfds;
234
235 char *buf_in, *buf_out;
236 struct iocb *iocb_in, *iocb_out;
237 int req_in = 0, req_out = 0;
238 bool ready;
239
240 if (argc != 2) {
241 printf("ffs directory not specified!\n");
242 return 1;
243 }
244
245 ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
246 if (!ep_path) {
247 perror("malloc");
248 return 1;
249 }
250
251 /* open endpoint files */
252 sprintf(ep_path, "%s/ep0", argv[1]);
253 ep0 = open(ep_path, O_RDWR);
254 if (ep0 < 0) {
255 perror("unable to open ep0");
256 return 1;
257 }
258 if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
259 perror("unable do write descriptors");
260 return 1;
261 }
262 if (write(ep0, &strings, sizeof(strings)) < 0) {
263 perror("unable to write strings");
264 return 1;
265 }
266 for (i = 0; i < 2; ++i) {
267 sprintf(ep_path, "%s/ep%d", argv[1], i+1);
268 ep[i] = open(ep_path, O_RDWR);
269 if (ep[i] < 0) {
270 printf("unable to open ep%d: %s\n", i+1,
271 strerror(errno));
272 return 1;
273 }
274 }
275
276 free(ep_path);
277
278 memset(&ctx, 0, sizeof(ctx));
279 /* setup aio context to handle up to 2 requests */
280 if (io_setup(2, &ctx) < 0) {
281 perror("unable to setup aio");
282 return 1;
283 }
284
285 evfd = eventfd(0, 0);
286 if (evfd < 0) {
287 perror("unable to open eventfd");
288 return 1;
289 }
290
291 /* alloc buffers and requests */
292 buf_in = malloc(BUF_LEN);
293 buf_out = malloc(BUF_LEN);
294 iocb_in = malloc(sizeof(*iocb_in));
295 iocb_out = malloc(sizeof(*iocb_out));
296
297 while (1) {
298 FD_ZERO(&rfds);
299 FD_SET(ep0, &rfds);
300 FD_SET(evfd, &rfds);
301
302 ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
303 &rfds, NULL, NULL, NULL);
304 if (ret < 0) {
305 if (errno == EINTR)
306 continue;
307 perror("select");
308 break;
309 }
310
311 if (FD_ISSET(ep0, &rfds))
312 handle_ep0(ep0, &ready);
313
314 /* we are waiting for function ENABLE */
315 if (!ready)
316 continue;
317
318 /* if something was submitted we wait for event */
319 if (FD_ISSET(evfd, &rfds)) {
320 uint64_t ev_cnt;
321 ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
322 if (ret < 0) {
323 perror("unable to read eventfd");
324 break;
325 }
326
327 struct io_event e[2];
328 /* we wait for one event */
329 ret = io_getevents(ctx, 1, 2, e, NULL);
330 /* if we got event */
331 for (i = 0; i < ret; ++i) {
332 if (e[i].obj->aio_fildes == ep[0]) {
333 printf("ev=in; ret=%lu\n", e[i].res);
334 req_in = 0;
335 } else if (e[i].obj->aio_fildes == ep[1]) {
336 printf("ev=out; ret=%lu\n", e[i].res);
337 req_out = 0;
338 }
339 }
340 }
341
342 if (!req_in) { /* if IN transfer not requested*/
343 /* prepare write request */
344 io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
345 /* enable eventfd notification */
346 iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
347 iocb_in->u.c.resfd = evfd;
348 /* submit table of requests */
349 ret = io_submit(ctx, 1, &iocb_in);
350 if (ret >= 0) { /* if ret > 0 request is queued */
351 req_in = 1;
352 printf("submit: in\n");
353 } else
354 perror("unable to submit request");
355 }
356 if (!req_out) { /* if OUT transfer not requested */
357 /* prepare read request */
358 io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
359 /* enable eventfs notification */
360 iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
361 iocb_out->u.c.resfd = evfd;
362 /* submit table of requests */
363 ret = io_submit(ctx, 1, &iocb_out);
364 if (ret >= 0) { /* if ret > 0 request is queued */
365 req_out = 1;
366 printf("submit: out\n");
367 } else
368 perror("unable to submit request");
369 }
370 }
371
372 /* free resources */
373
374 io_destroy(ctx);
375
376 free(buf_in);
377 free(buf_out);
378 free(iocb_in);
379 free(iocb_out);
380
381 for (i = 0; i < 2; ++i)
382 close(ep[i]);
383 close(ep0);
384
385 return 0;
386 }
387