123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- /*
- * 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/>
- */
- #define _BSD_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
- #define BUFS_MAX 128
- #define AIO_MAX (BUFS_MAX*2)
- /******************** 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 = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
- .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
- FUNCTIONFS_HAS_HS_DESC),
- .length = htole32(sizeof(descriptors)),
- },
- .fs_count = htole32(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 = htole32(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 = htole16(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 = htole16(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 = htole32(FUNCTIONFS_STRINGS_MAGIC),
- .length = htole32(sizeof(strings)),
- .str_count = htole32(1),
- .lang_count = htole32(1),
- },
- .lang0 = {
- htole16(0x0409), /* en-us */
- STR_INTERFACE,
- },
- };
- /********************** Buffer structure *******************************/
- struct io_buffer {
- struct iocb **iocb;
- unsigned char **buf;
- unsigned cnt;
- unsigned len;
- unsigned requested;
- };
- /******************** 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)
- {
- int ret;
- struct usb_functionfs_event event;
- 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;
- }
- }
- void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)
- {
- unsigned i;
- iobuf->buf = malloc(n*sizeof(*iobuf->buf));
- iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
- iobuf->cnt = n;
- iobuf->len = len;
- iobuf->requested = 0;
- for (i = 0; i < n; ++i) {
- iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
- iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
- }
- iobuf->cnt = n;
- }
- void delete_bufs(struct io_buffer *iobuf)
- {
- unsigned i;
- for (i = 0; i < iobuf->cnt; ++i) {
- free(iobuf->buf[i]);
- free(iobuf->iocb[i]);
- }
- free(iobuf->buf);
- free(iobuf->iocb);
- }
- int main(int argc, char *argv[])
- {
- int ret;
- unsigned i, j;
- char *ep_path;
- int ep0, ep1;
- io_context_t ctx;
- int evfd;
- fd_set rfds;
- struct io_buffer iobuf[2];
- int actual = 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;
- }
- sprintf(ep_path, "%s/ep1", argv[1]);
- ep1 = open(ep_path, O_RDWR);
- if (ep1 < 0) {
- perror("unable to open ep1");
- return 1;
- }
- free(ep_path);
- memset(&ctx, 0, sizeof(ctx));
- /* setup aio context to handle up to AIO_MAX requests */
- if (io_setup(AIO_MAX, &ctx) < 0) {
- perror("unable to setup aio");
- return 1;
- }
- evfd = eventfd(0, 0);
- if (evfd < 0) {
- perror("unable to open eventfd");
- return 1;
- }
- for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
- init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);
- 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;
- /*
- * when we're preparing new data to submit,
- * second buffer being transmitted
- */
- for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {
- if (iobuf[i].requested)
- continue;
- /* prepare requests */
- for (j = 0; j < iobuf[i].cnt; ++j) {
- io_prep_pwrite(iobuf[i].iocb[j], ep1,
- iobuf[i].buf[j],
- iobuf[i].len, 0);
- /* enable eventfd notification */
- iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;
- iobuf[i].iocb[j]->u.c.resfd = evfd;
- }
- /* submit table of requests */
- ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);
- if (ret >= 0) {
- iobuf[i].requested = ret;
- printf("submit: %d requests buf: %d\n", ret, i);
- } else
- perror("unable to submit requests");
- }
- /* if event is ready to read */
- if (!FD_ISSET(evfd, &rfds))
- continue;
- 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[BUFS_MAX];
- /* we read aio events */
- ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);
- if (ret > 0) /* if we got events */
- iobuf[actual].requested -= ret;
- /* if all req's from iocb completed */
- if (!iobuf[actual].requested)
- actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));
- }
- /* free resources */
- for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
- delete_bufs(&iobuf[i]);
- io_destroy(ctx);
- close(ep1);
- close(ep0);
- return 0;
- }
|