aio_simple.c (9108B)
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#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 52/******************** Descriptors and Strings *******************************/ 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), /* en-us */ 135 STR_INTERFACE, 136 }, 137}; 138 139/******************** Endpoints handling *******************************/ 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 /* "/ep#" */ + 1 /* '\0' */); 228 if (!ep_path) { 229 perror("malloc"); 230 return 1; 231 } 232 233 /* open endpoint files */ 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 /* setup aio context to handle up to 2 requests */ 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 /* alloc buffers and requests */ 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 /* we are waiting for function ENABLE */ 297 if (!ready) 298 continue; 299 300 /* if something was submitted we wait for event */ 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 /* we wait for one event */ 311 ret = io_getevents(ctx, 1, 2, e, NULL); 312 /* if we got event */ 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) { /* if IN transfer not requested*/ 325 /* prepare write request */ 326 io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0); 327 /* enable eventfd notification */ 328 iocb_in->u.c.flags |= IOCB_FLAG_RESFD; 329 iocb_in->u.c.resfd = evfd; 330 /* submit table of requests */ 331 ret = io_submit(ctx, 1, &iocb_in); 332 if (ret >= 0) { /* if ret > 0 request is queued */ 333 req_in = 1; 334 printf("submit: in\n"); 335 } else 336 perror("unable to submit request"); 337 } 338 if (!req_out) { /* if OUT transfer not requested */ 339 /* prepare read request */ 340 io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0); 341 /* enable eventfs notification */ 342 iocb_out->u.c.flags |= IOCB_FLAG_RESFD; 343 iocb_out->u.c.resfd = evfd; 344 /* submit table of requests */ 345 ret = io_submit(ctx, 1, &iocb_out); 346 if (ret >= 0) { /* if ret > 0 request is queued */ 347 req_out = 1; 348 printf("submit: out\n"); 349 } else 350 perror("unable to submit request"); 351 } 352 } 353 354 /* free resources */ 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}