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 /* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */
30 #define _DEFAULT_SOURCE /* for endian.h */
39 #include <sys/ioctl.h>
41 #include <sys/types.h>
45 #include <sys/eventfd.h>
48 #define IOCB_FLAG_RESFD (1 << 0)
50 #include <linux/usb/functionfs.h>
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.
60 #if BYTE_ORDER == __LITTLE_ENDIAN
61 #define cpu_to_le16(x) (x)
62 #define cpu_to_le32(x) (x)
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))
70 /******************** Descriptors and Strings *******************************/
73 struct usb_functionfs_descs_head_v2 header
;
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
= {
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
)),
88 .fs_count
= cpu_to_le32(3),
91 .bLength
= sizeof(descriptors
.fs_descs
.intf
),
92 .bDescriptorType
= USB_DT_INTERFACE
,
94 .bInterfaceClass
= USB_CLASS_VENDOR_SPEC
,
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
,
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
,
110 .hs_count
= cpu_to_le32(3),
113 .bLength
= sizeof(descriptors
.hs_descs
.intf
),
114 .bDescriptorType
= USB_DT_INTERFACE
,
116 .bInterfaceClass
= USB_CLASS_VENDOR_SPEC
,
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),
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),
136 #define STR_INTERFACE "AIO Test"
138 static const struct {
139 struct usb_functionfs_strings_head header
;
142 const char str1
[sizeof(STR_INTERFACE
)];
143 } __attribute__ ((__packed__
)) lang0
;
144 } __attribute__ ((__packed__
)) strings
= {
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),
152 cpu_to_le16(0x0409), /* en-us */
157 /******************** Endpoints handling *******************************/
159 static void display_event(struct usb_functionfs_event
*event
)
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",
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
]);
182 static void handle_ep0(int ep0
, bool *ready
)
184 struct usb_functionfs_event event
;
187 struct pollfd pfds
[1];
189 pfds
[0].events
= POLLIN
;
191 ret
= poll(pfds
, 1, 0);
193 if (ret
&& (pfds
[0].revents
& POLLIN
)) {
194 ret
= read(ep0
, &event
, sizeof(event
));
196 perror("unable to read event from ep0");
199 display_event(&event
);
200 switch (event
.type
) {
201 case FUNCTIONFS_SETUP
:
202 if (event
.u
.setup
.bRequestType
& USB_DIR_IN
)
208 case FUNCTIONFS_ENABLE
:
212 case FUNCTIONFS_DISABLE
:
222 int main(int argc
, char *argv
[])
235 char *buf_in
, *buf_out
;
236 struct iocb
*iocb_in
, *iocb_out
;
237 int req_in
= 0, req_out
= 0;
241 printf("ffs directory not specified!\n");
245 ep_path
= malloc(strlen(argv
[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
251 /* open endpoint files */
252 sprintf(ep_path
, "%s/ep0", argv
[1]);
253 ep0
= open(ep_path
, O_RDWR
);
255 perror("unable to open ep0");
258 if (write(ep0
, &descriptors
, sizeof(descriptors
)) < 0) {
259 perror("unable do write descriptors");
262 if (write(ep0
, &strings
, sizeof(strings
)) < 0) {
263 perror("unable to write strings");
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
);
270 printf("unable to open ep%d: %s\n", i
+1,
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");
285 evfd
= eventfd(0, 0);
287 perror("unable to open eventfd");
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
));
302 ret
= select(((ep0
> evfd
) ? ep0
: evfd
)+1,
303 &rfds
, NULL
, NULL
, NULL
);
311 if (FD_ISSET(ep0
, &rfds
))
312 handle_ep0(ep0
, &ready
);
314 /* we are waiting for function ENABLE */
318 /* if something was submitted we wait for event */
319 if (FD_ISSET(evfd
, &rfds
)) {
321 ret
= read(evfd
, &ev_cnt
, sizeof(ev_cnt
));
323 perror("unable to read eventfd");
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
);
335 } else if (e
[i
].obj
->aio_fildes
== ep
[1]) {
336 printf("ev=out; ret=%lu\n", e
[i
].res
);
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 */
352 printf("submit: in\n");
354 perror("unable to submit request");
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 */
366 printf("submit: out\n");
368 perror("unable to submit request");
381 for (i
= 0; i
< 2; ++i
)