387 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/* $(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 819253 54/*55 * cpu_to_le16/32 are used when initializing structures, a context where a56 * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way57 * that allows them to be used when initializing structures.58 */59 60#if BYTE_ORDER == __LITTLE_ENDIAN61#define cpu_to_le16(x) (x)62#define cpu_to_le32(x) (x)63#else64#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#endif69 70/******************** Descriptors and Strings *******************************/71 72static 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 138static 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 159static 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 182static 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 else205 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 222int 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 } else354 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 } else368 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