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 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 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 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