381 lines · c
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, or5 * distribute this software, either in source code form or as a compiled6 * binary, for any purpose, commercial or non-commercial, and by any7 * means.8 *9 * In jurisdictions that recognize copyright laws, the author or authors10 * of this software dedicate any and all copyright interest in the11 * software to the public domain. We make this dedication for the benefit12 * of the public at large and to the detriment of our heirs and13 * successors. We intend this dedication to be an overt act of14 * relinquishment in perpetuity of all present and future rights to this15 * 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 OF19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR21 * 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 OR23 * 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 819251#define BUFS_MAX 12852#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 else192 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 transmitted326 */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 } else345 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}381