Path: blob/master/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
26292 views
/*1* This is free and unencumbered software released into the public domain.2*3* Anyone is free to copy, modify, publish, use, compile, sell, or4* distribute this software, either in source code form or as a compiled5* binary, for any purpose, commercial or non-commercial, and by any6* means.7*8* In jurisdictions that recognize copyright laws, the author or authors9* of this software dedicate any and all copyright interest in the10* software to the public domain. We make this dedication for the benefit11* of the public at large and to the detriment of our heirs and12* successors. We intend this dedication to be an overt act of13* relinquishment in perpetuity of all present and future rights to this14* software under copyright law.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.19* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR20* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,21* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR22* OTHER DEALINGS IN THE SOFTWARE.23*24* For more information, please refer to <http://unlicense.org/>25*/2627#define _BSD_SOURCE /* for endian.h */2829#include <endian.h>30#include <errno.h>31#include <fcntl.h>32#include <stdarg.h>33#include <stdio.h>34#include <stdlib.h>35#include <string.h>36#include <sys/ioctl.h>37#include <sys/stat.h>38#include <sys/types.h>39#include <sys/poll.h>40#include <unistd.h>41#include <stdbool.h>42#include <sys/eventfd.h>4344#include "libaio.h"45#define IOCB_FLAG_RESFD (1 << 0)4647#include <linux/usb/functionfs.h>4849#define BUF_LEN 819250#define BUFS_MAX 12851#define AIO_MAX (BUFS_MAX*2)5253/******************** Descriptors and Strings *******************************/5455static const struct {56struct usb_functionfs_descs_head_v2 header;57__le32 fs_count;58__le32 hs_count;59struct {60struct usb_interface_descriptor intf;61struct usb_endpoint_descriptor_no_audio bulk_sink;62struct usb_endpoint_descriptor_no_audio bulk_source;63} __attribute__ ((__packed__)) fs_descs, hs_descs;64} __attribute__ ((__packed__)) descriptors = {65.header = {66.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),67.flags = htole32(FUNCTIONFS_HAS_FS_DESC |68FUNCTIONFS_HAS_HS_DESC),69.length = htole32(sizeof(descriptors)),70},71.fs_count = htole32(3),72.fs_descs = {73.intf = {74.bLength = sizeof(descriptors.fs_descs.intf),75.bDescriptorType = USB_DT_INTERFACE,76.bNumEndpoints = 2,77.bInterfaceClass = USB_CLASS_VENDOR_SPEC,78.iInterface = 1,79},80.bulk_sink = {81.bLength = sizeof(descriptors.fs_descs.bulk_sink),82.bDescriptorType = USB_DT_ENDPOINT,83.bEndpointAddress = 1 | USB_DIR_IN,84.bmAttributes = USB_ENDPOINT_XFER_BULK,85},86.bulk_source = {87.bLength = sizeof(descriptors.fs_descs.bulk_source),88.bDescriptorType = USB_DT_ENDPOINT,89.bEndpointAddress = 2 | USB_DIR_OUT,90.bmAttributes = USB_ENDPOINT_XFER_BULK,91},92},93.hs_count = htole32(3),94.hs_descs = {95.intf = {96.bLength = sizeof(descriptors.hs_descs.intf),97.bDescriptorType = USB_DT_INTERFACE,98.bNumEndpoints = 2,99.bInterfaceClass = USB_CLASS_VENDOR_SPEC,100.iInterface = 1,101},102.bulk_sink = {103.bLength = sizeof(descriptors.hs_descs.bulk_sink),104.bDescriptorType = USB_DT_ENDPOINT,105.bEndpointAddress = 1 | USB_DIR_IN,106.bmAttributes = USB_ENDPOINT_XFER_BULK,107.wMaxPacketSize = htole16(512),108},109.bulk_source = {110.bLength = sizeof(descriptors.hs_descs.bulk_source),111.bDescriptorType = USB_DT_ENDPOINT,112.bEndpointAddress = 2 | USB_DIR_OUT,113.bmAttributes = USB_ENDPOINT_XFER_BULK,114.wMaxPacketSize = htole16(512),115},116},117};118119#define STR_INTERFACE "AIO Test"120121static const struct {122struct usb_functionfs_strings_head header;123struct {124__le16 code;125const char str1[sizeof(STR_INTERFACE)];126} __attribute__ ((__packed__)) lang0;127} __attribute__ ((__packed__)) strings = {128.header = {129.magic = htole32(FUNCTIONFS_STRINGS_MAGIC),130.length = htole32(sizeof(strings)),131.str_count = htole32(1),132.lang_count = htole32(1),133},134.lang0 = {135htole16(0x0409), /* en-us */136STR_INTERFACE,137},138};139140/********************** Buffer structure *******************************/141142struct io_buffer {143struct iocb **iocb;144unsigned char **buf;145unsigned cnt;146unsigned len;147unsigned requested;148};149150/******************** Endpoints handling *******************************/151152static void display_event(struct usb_functionfs_event *event)153{154static const char *const names[] = {155[FUNCTIONFS_BIND] = "BIND",156[FUNCTIONFS_UNBIND] = "UNBIND",157[FUNCTIONFS_ENABLE] = "ENABLE",158[FUNCTIONFS_DISABLE] = "DISABLE",159[FUNCTIONFS_SETUP] = "SETUP",160[FUNCTIONFS_SUSPEND] = "SUSPEND",161[FUNCTIONFS_RESUME] = "RESUME",162};163switch (event->type) {164case FUNCTIONFS_BIND:165case FUNCTIONFS_UNBIND:166case FUNCTIONFS_ENABLE:167case FUNCTIONFS_DISABLE:168case FUNCTIONFS_SETUP:169case FUNCTIONFS_SUSPEND:170case FUNCTIONFS_RESUME:171printf("Event %s\n", names[event->type]);172}173}174175static void handle_ep0(int ep0, bool *ready)176{177int ret;178struct usb_functionfs_event event;179180ret = read(ep0, &event, sizeof(event));181if (!ret) {182perror("unable to read event from ep0");183return;184}185display_event(&event);186switch (event.type) {187case FUNCTIONFS_SETUP:188if (event.u.setup.bRequestType & USB_DIR_IN)189write(ep0, NULL, 0);190else191read(ep0, NULL, 0);192break;193194case FUNCTIONFS_ENABLE:195*ready = true;196break;197198case FUNCTIONFS_DISABLE:199*ready = false;200break;201202default:203break;204}205}206207void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)208{209unsigned i;210iobuf->buf = malloc(n*sizeof(*iobuf->buf));211iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));212iobuf->cnt = n;213iobuf->len = len;214iobuf->requested = 0;215for (i = 0; i < n; ++i) {216iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));217iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));218}219iobuf->cnt = n;220}221222void delete_bufs(struct io_buffer *iobuf)223{224unsigned i;225for (i = 0; i < iobuf->cnt; ++i) {226free(iobuf->buf[i]);227free(iobuf->iocb[i]);228}229free(iobuf->buf);230free(iobuf->iocb);231}232233int main(int argc, char *argv[])234{235int ret;236unsigned i, j;237char *ep_path;238239int ep0, ep1;240241io_context_t ctx;242243int evfd;244fd_set rfds;245246struct io_buffer iobuf[2];247int actual = 0;248bool ready;249250if (argc != 2) {251printf("ffs directory not specified!\n");252return 1;253}254255ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);256if (!ep_path) {257perror("malloc");258return 1;259}260261/* open endpoint files */262sprintf(ep_path, "%s/ep0", argv[1]);263ep0 = open(ep_path, O_RDWR);264if (ep0 < 0) {265perror("unable to open ep0");266return 1;267}268if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {269perror("unable do write descriptors");270return 1;271}272if (write(ep0, &strings, sizeof(strings)) < 0) {273perror("unable to write strings");274return 1;275}276sprintf(ep_path, "%s/ep1", argv[1]);277ep1 = open(ep_path, O_RDWR);278if (ep1 < 0) {279perror("unable to open ep1");280return 1;281}282283free(ep_path);284285memset(&ctx, 0, sizeof(ctx));286/* setup aio context to handle up to AIO_MAX requests */287if (io_setup(AIO_MAX, &ctx) < 0) {288perror("unable to setup aio");289return 1;290}291292evfd = eventfd(0, 0);293if (evfd < 0) {294perror("unable to open eventfd");295return 1;296}297298for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)299init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);300301while (1) {302FD_ZERO(&rfds);303FD_SET(ep0, &rfds);304FD_SET(evfd, &rfds);305306ret = select(((ep0 > evfd) ? ep0 : evfd)+1,307&rfds, NULL, NULL, NULL);308if (ret < 0) {309if (errno == EINTR)310continue;311perror("select");312break;313}314315if (FD_ISSET(ep0, &rfds))316handle_ep0(ep0, &ready);317318/* we are waiting for function ENABLE */319if (!ready)320continue;321322/*323* when we're preparing new data to submit,324* second buffer being transmitted325*/326for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {327if (iobuf[i].requested)328continue;329/* prepare requests */330for (j = 0; j < iobuf[i].cnt; ++j) {331io_prep_pwrite(iobuf[i].iocb[j], ep1,332iobuf[i].buf[j],333iobuf[i].len, 0);334/* enable eventfd notification */335iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;336iobuf[i].iocb[j]->u.c.resfd = evfd;337}338/* submit table of requests */339ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);340if (ret >= 0) {341iobuf[i].requested = ret;342printf("submit: %d requests buf: %d\n", ret, i);343} else344perror("unable to submit requests");345}346347/* if event is ready to read */348if (!FD_ISSET(evfd, &rfds))349continue;350351uint64_t ev_cnt;352ret = read(evfd, &ev_cnt, sizeof(ev_cnt));353if (ret < 0) {354perror("unable to read eventfd");355break;356}357358struct io_event e[BUFS_MAX];359/* we read aio events */360ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);361if (ret > 0) /* if we got events */362iobuf[actual].requested -= ret;363364/* if all req's from iocb completed */365if (!iobuf[actual].requested)366actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));367}368369/* free resources */370371for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)372delete_bufs(&iobuf[i]);373io_destroy(ctx);374375close(ep1);376close(ep0);377378return 0;379}380381382