aio_multibuff.c (9268B)
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#define BUFS_MAX 128 52#define AIO_MAX (BUFS_MAX*2) 53 54/******************** Descriptors and Strings *******************************/ 55 56static const struct { 57 struct usb_functionfs_descs_head_v2 header; 58 __le32 fs_count; 59 __le32 hs_count; 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 = { 67 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), 68 .flags = htole32(FUNCTIONFS_HAS_FS_DESC | 69 FUNCTIONFS_HAS_HS_DESC), 70 .length = htole32(sizeof(descriptors)), 71 }, 72 .fs_count = htole32(3), 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 }, 94 .hs_count = htole32(3), 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 122static 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 143struct 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 153static 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 176static 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 208void 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 223void 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 234int 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 requests"); 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}