1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#define _BSD_SOURCE
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
53
54static const struct {
55 struct usb_functionfs_descs_head_v2 header;
56 __le32 fs_count;
57 __le32 hs_count;
58 struct {
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 = {
64 .header = {
65 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
66 .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
67 FUNCTIONFS_HAS_HS_DESC),
68 .length = htole32(sizeof(descriptors)),
69 },
70 .fs_count = htole32(3),
71 .fs_descs = {
72 .intf = {
73 .bLength = sizeof(descriptors.fs_descs.intf),
74 .bDescriptorType = USB_DT_INTERFACE,
75 .bNumEndpoints = 2,
76 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
77 .iInterface = 1,
78 },
79 .bulk_sink = {
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,
84 },
85 .bulk_source = {
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,
90 },
91 },
92 .hs_count = htole32(3),
93 .hs_descs = {
94 .intf = {
95 .bLength = sizeof(descriptors.hs_descs.intf),
96 .bDescriptorType = USB_DT_INTERFACE,
97 .bNumEndpoints = 2,
98 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
99 .iInterface = 1,
100 },
101 .bulk_sink = {
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),
107 },
108 .bulk_source = {
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),
114 },
115 },
116};
117
118#define STR_INTERFACE "AIO Test"
119
120static const struct {
121 struct usb_functionfs_strings_head header;
122 struct {
123 __le16 code;
124 const char str1[sizeof(STR_INTERFACE)];
125 } __attribute__ ((__packed__)) lang0;
126} __attribute__ ((__packed__)) strings = {
127 .header = {
128 .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
129 .length = htole32(sizeof(strings)),
130 .str_count = htole32(1),
131 .lang_count = htole32(1),
132 },
133 .lang0 = {
134 htole16(0x0409),
135 STR_INTERFACE,
136 },
137};
138
139
140
141static void display_event(struct usb_functionfs_event *event)
142{
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",
151 };
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]);
161 }
162}
163
164static void handle_ep0(int ep0, bool *ready)
165{
166 struct usb_functionfs_event event;
167 int ret;
168
169 struct pollfd pfds[1];
170 pfds[0].fd = ep0;
171 pfds[0].events = POLLIN;
172
173 ret = poll(pfds, 1, 0);
174
175 if (ret && (pfds[0].revents & POLLIN)) {
176 ret = read(ep0, &event, sizeof(event));
177 if (!ret) {
178 perror("unable to read event from ep0");
179 return;
180 }
181 display_event(&event);
182 switch (event.type) {
183 case FUNCTIONFS_SETUP:
184 if (event.u.setup.bRequestType & USB_DIR_IN)
185 write(ep0, NULL, 0);
186 else
187 read(ep0, NULL, 0);
188 break;
189
190 case FUNCTIONFS_ENABLE:
191 *ready = true;
192 break;
193
194 case FUNCTIONFS_DISABLE:
195 *ready = false;
196 break;
197
198 default:
199 break;
200 }
201 }
202}
203
204int main(int argc, char *argv[])
205{
206 int i, ret;
207 char *ep_path;
208
209 int ep0;
210 int ep[2];
211
212 io_context_t ctx;
213
214 int evfd;
215 fd_set rfds;
216
217 char *buf_in, *buf_out;
218 struct iocb *iocb_in, *iocb_out;
219 int req_in = 0, req_out = 0;
220 bool ready;
221
222 if (argc != 2) {
223 printf("ffs directory not specified!\n");
224 return 1;
225 }
226
227 ep_path = malloc(strlen(argv[1]) + 4 + 1 );
228 if (!ep_path) {
229 perror("malloc");
230 return 1;
231 }
232
233
234 sprintf(ep_path, "%s/ep0", argv[1]);
235 ep0 = open(ep_path, O_RDWR);
236 if (ep0 < 0) {
237 perror("unable to open ep0");
238 return 1;
239 }
240 if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
241 perror("unable do write descriptors");
242 return 1;
243 }
244 if (write(ep0, &strings, sizeof(strings)) < 0) {
245 perror("unable to write strings");
246 return 1;
247 }
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);
251 if (ep[i] < 0) {
252 printf("unable to open ep%d: %s\n", i+1,
253 strerror(errno));
254 return 1;
255 }
256 }
257
258 free(ep_path);
259
260 memset(&ctx, 0, sizeof(ctx));
261
262 if (io_setup(2, &ctx) < 0) {
263 perror("unable to setup aio");
264 return 1;
265 }
266
267 evfd = eventfd(0, 0);
268 if (evfd < 0) {
269 perror("unable to open eventfd");
270 return 1;
271 }
272
273
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));
278
279 while (1) {
280 FD_ZERO(&rfds);
281 FD_SET(ep0, &rfds);
282 FD_SET(evfd, &rfds);
283
284 ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
285 &rfds, NULL, NULL, NULL);
286 if (ret < 0) {
287 if (errno == EINTR)
288 continue;
289 perror("select");
290 break;
291 }
292
293 if (FD_ISSET(ep0, &rfds))
294 handle_ep0(ep0, &ready);
295
296
297 if (!ready)
298 continue;
299
300
301 if (FD_ISSET(evfd, &rfds)) {
302 uint64_t ev_cnt;
303 ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
304 if (ret < 0) {
305 perror("unable to read eventfd");
306 break;
307 }
308
309 struct io_event e[2];
310
311 ret = io_getevents(ctx, 1, 2, e, NULL);
312
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);
316 req_in = 0;
317 } else if (e[i].obj->aio_fildes == ep[1]) {
318 printf("ev=out; ret=%lu\n", e[i].res);
319 req_out = 0;
320 }
321 }
322 }
323
324 if (!req_in) {
325
326 io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
327
328 iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
329 iocb_in->u.c.resfd = evfd;
330
331 ret = io_submit(ctx, 1, &iocb_in);
332 if (ret >= 0) {
333 req_in = 1;
334 printf("submit: in\n");
335 } else
336 perror("unable to submit request");
337 }
338 if (!req_out) {
339
340 io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
341
342 iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
343 iocb_out->u.c.resfd = evfd;
344
345 ret = io_submit(ctx, 1, &iocb_out);
346 if (ret >= 0) {
347 req_out = 1;
348 printf("submit: out\n");
349 } else
350 perror("unable to submit request");
351 }
352 }
353
354
355
356 io_destroy(ctx);
357
358 free(buf_in);
359 free(buf_out);
360 free(iocb_in);
361 free(iocb_out);
362
363 for (i = 0; i < 2; ++i)
364 close(ep[i]);
365 close(ep0);
366
367 return 0;
368}
369