Commit | Line | Data |
---|---|---|
aa491320 RB |
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 | ||
b34e08d5 RB |
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 { | |
0ebe9910 RB |
57 | struct usb_functionfs_descs_head_v2 header; |
58 | __le32 fs_count; | |
59 | __le32 hs_count; | |
b34e08d5 RB |
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 = { | |
0ebe9910 RB |
67 | .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), |
68 | .flags = htole32(FUNCTIONFS_HAS_FS_DESC | | |
69 | FUNCTIONFS_HAS_HS_DESC), | |
b34e08d5 | 70 | .length = htole32(sizeof(descriptors)), |
b34e08d5 | 71 | }, |
0ebe9910 | 72 | .fs_count = htole32(3), |
b34e08d5 RB |
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 | }, | |
0ebe9910 | 94 | .hs_count = htole32(3), |
b34e08d5 RB |
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 header; | |
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); | |
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[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); | |
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 reqests"); | |
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[i]); | |
374 | io_destroy(ctx); | |
375 | ||
376 | close(ep1); | |
377 | close(ep0); | |
378 | ||
379 | return 0; | |
380 | } |