/* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to <http://unlicense.org/> */ /* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */ #define _DEFAULT_SOURCE /* for endian.h */ #include <endian.h> #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/poll.h> #include <unistd.h> #include <stdbool.h> #include <sys/eventfd.h> #include "libaio.h" #define IOCB_FLAG_RESFD (1 << 0) #include <linux/usb/functionfs.h> #define BUF_LEN 8192 /* * cpu_to_le16/32 are used when initializing structures, a context where a * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way * that allows them to be used when initializing structures. */ #if BYTE_ORDER == __LITTLE_ENDIAN #define cpu_to_le16(x) (x) #define cpu_to_le32(x) (x) #else #define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) #define cpu_to_le32(x) \ ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) #endif /******************** Descriptors and Strings *******************************/ static const struct { struct usb_functionfs_descs_head_v2 header; __le32 fs_count; __le32 hs_count; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio bulk_sink; struct usb_endpoint_descriptor_no_audio bulk_source; } __attribute__ ((__packed__)) fs_descs, hs_descs; } __attribute__ ((__packed__)) descriptors = { .header = { .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC), .length = cpu_to_le32(sizeof(descriptors)), }, .fs_count = cpu_to_le32(3), .fs_descs = { .intf = { .bLength = sizeof(descriptors.fs_descs.intf), .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .iInterface = 1, }, .bulk_sink = { .bLength = sizeof(descriptors.fs_descs.bulk_sink), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .bulk_source = { .bLength = sizeof(descriptors.fs_descs.bulk_source), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }, }, .hs_count = cpu_to_le32(3), .hs_descs = { .intf = { .bLength = sizeof(descriptors.hs_descs.intf), .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .iInterface = 1, }, .bulk_sink = { .bLength = sizeof(descriptors.hs_descs.bulk_sink), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }, .bulk_source = { .bLength = sizeof(descriptors.hs_descs.bulk_source), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }, }, }; #define STR_INTERFACE "AIO Test" static const struct { struct usb_functionfs_strings_head header; struct { __le16 code; const char str1[sizeof(STR_INTERFACE)]; } __attribute__ ((__packed__)) lang0; } __attribute__ ((__packed__)) strings = { .header = { .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), .length = cpu_to_le32(sizeof(strings)), .str_count = cpu_to_le32(1), .lang_count = cpu_to_le32(1), }, .lang0 = { cpu_to_le16(0x0409), /* en-us */ STR_INTERFACE, }, }; /******************** Endpoints handling *******************************/ static void display_event(struct usb_functionfs_event *event) { static const char *const names[] = { [FUNCTIONFS_BIND] = "BIND", [FUNCTIONFS_UNBIND] = "UNBIND", [FUNCTIONFS_ENABLE] = "ENABLE", [FUNCTIONFS_DISABLE] = "DISABLE", [FUNCTIONFS_SETUP] = "SETUP", [FUNCTIONFS_SUSPEND] = "SUSPEND", [FUNCTIONFS_RESUME] = "RESUME", }; switch (event->type) { case FUNCTIONFS_BIND: case FUNCTIONFS_UNBIND: case FUNCTIONFS_ENABLE: case FUNCTIONFS_DISABLE: case FUNCTIONFS_SETUP: case FUNCTIONFS_SUSPEND: case FUNCTIONFS_RESUME: printf("Event %s\n", names[event->type]); } } static void handle_ep0(int ep0, bool *ready) { struct usb_functionfs_event event; int ret; struct pollfd pfds[1]; pfds[0].fd = ep0; pfds[0].events = POLLIN; ret = poll(pfds, 1, 0); if (ret && (pfds[0].revents & POLLIN)) { ret = read(ep0, &event, sizeof(event)); if (!ret) { perror("unable to read event from ep0"); return; } display_event(&event); switch (event.type) { case FUNCTIONFS_SETUP: if (event.u.setup.bRequestType & USB_DIR_IN) write(ep0, NULL, 0); else read(ep0, NULL, 0); break; case FUNCTIONFS_ENABLE: *ready = true; break; case FUNCTIONFS_DISABLE: *ready = false; break; default: break; } } } int main(int argc, char *argv[]) { int i, ret; char *ep_path; int ep0; int ep[2]; io_context_t ctx; int evfd; fd_set rfds; char *buf_in, *buf_out; struct iocb *iocb_in, *iocb_out; int req_in = 0, req_out = 0; bool ready; if (argc != 2) { printf("ffs directory not specified!\n"); return 1; } ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */); if (!ep_path) { perror("malloc"); return 1; } /* open endpoint files */ sprintf(ep_path, "%s/ep0", argv[1]); ep0 = open(ep_path, O_RDWR); if (ep0 < 0) { perror("unable to open ep0"); return 1; } if (write(ep0, &descriptors, sizeof(descriptors)) < 0) { perror("unable do write descriptors"); return 1; } if (write(ep0, &strings, sizeof(strings)) < 0) { perror("unable to write strings"); return 1; } for (i = 0; i < 2; ++i) { sprintf(ep_path, "%s/ep%d", argv[1], i+1); ep[i] = open(ep_path, O_RDWR); if (ep[i] < 0) { printf("unable to open ep%d: %s\n", i+1, strerror(errno)); return 1; } } free(ep_path); memset(&ctx, 0, sizeof(ctx)); /* setup aio context to handle up to 2 requests */ if (io_setup(2, &ctx) < 0) { perror("unable to setup aio"); return 1; } evfd = eventfd(0, 0); if (evfd < 0) { perror("unable to open eventfd"); return 1; } /* alloc buffers and requests */ buf_in = malloc(BUF_LEN); buf_out = malloc(BUF_LEN); iocb_in = malloc(sizeof(*iocb_in)); iocb_out = malloc(sizeof(*iocb_out)); while (1) { FD_ZERO(&rfds); FD_SET(ep0, &rfds); FD_SET(evfd, &rfds); ret = select(((ep0 > evfd) ? ep0 : evfd)+1, &rfds, NULL, NULL, NULL); if (ret < 0) { if (errno == EINTR) continue; perror("select"); break; } if (FD_ISSET(ep0, &rfds)) handle_ep0(ep0, &ready); /* we are waiting for function ENABLE */ if (!ready) continue; /* if something was submitted we wait for event */ if (FD_ISSET(evfd, &rfds)) { uint64_t ev_cnt; ret = read(evfd, &ev_cnt, sizeof(ev_cnt)); if (ret < 0) { perror("unable to read eventfd"); break; } struct io_event e[2]; /* we wait for one event */ ret = io_getevents(ctx, 1, 2, e, NULL); /* if we got event */ for (i = 0; i < ret; ++i) { if (e[i].obj->aio_fildes == ep[0]) { printf("ev=in; ret=%lu\n", e[i].res); req_in = 0; } else if (e[i].obj->aio_fildes == ep[1]) { printf("ev=out; ret=%lu\n", e[i].res); req_out = 0; } } } if (!req_in) { /* if IN transfer not requested*/ /* prepare write request */ io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0); /* enable eventfd notification */ iocb_in->u.c.flags |= IOCB_FLAG_RESFD; iocb_in->u.c.resfd = evfd; /* submit table of requests */ ret = io_submit(ctx, 1, &iocb_in); if (ret >= 0) { /* if ret > 0 request is queued */ req_in = 1; printf("submit: in\n"); } else perror("unable to submit request"); } if (!req_out) { /* if OUT transfer not requested */ /* prepare read request */ io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0); /* enable eventfs notification */ iocb_out->u.c.flags |= IOCB_FLAG_RESFD; iocb_out->u.c.resfd = evfd; /* submit table of requests */ ret = io_submit(ctx, 1, &iocb_out); if (ret >= 0) { /* if ret > 0 request is queued */ req_out = 1; printf("submit: out\n"); } else perror("unable to submit request"); } } /* free resources */ io_destroy(ctx); free(buf_in); free(buf_out); free(iocb_in); free(iocb_out); for (i = 0; i < 2; ++i) close(ep[i]); close(ep0); return 0; }