Path: blob/master/tools/usb/ffs-aio-example/simple/device_app/aio_simple.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/* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */2829#define _DEFAULT_SOURCE /* for endian.h */3031#include <endian.h>32#include <errno.h>33#include <fcntl.h>34#include <stdarg.h>35#include <stdio.h>36#include <stdlib.h>37#include <string.h>38#include <sys/ioctl.h>39#include <sys/stat.h>40#include <sys/types.h>41#include <sys/poll.h>42#include <unistd.h>43#include <stdbool.h>44#include <sys/eventfd.h>4546#include "libaio.h"47#define IOCB_FLAG_RESFD (1 << 0)4849#include <linux/usb/functionfs.h>5051#define BUF_LEN 81925253/*54* cpu_to_le16/32 are used when initializing structures, a context where a55* function call is not allowed. To solve this, we code cpu_to_le16/32 in a way56* that allows them to be used when initializing structures.57*/5859#if BYTE_ORDER == __LITTLE_ENDIAN60#define cpu_to_le16(x) (x)61#define cpu_to_le32(x) (x)62#else63#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))64#define cpu_to_le32(x) \65((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \66(((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))67#endif6869/******************** Descriptors and Strings *******************************/7071static const struct {72struct usb_functionfs_descs_head_v2 header;73__le32 fs_count;74__le32 hs_count;75struct {76struct usb_interface_descriptor intf;77struct usb_endpoint_descriptor_no_audio bulk_sink;78struct usb_endpoint_descriptor_no_audio bulk_source;79} __attribute__ ((__packed__)) fs_descs, hs_descs;80} __attribute__ ((__packed__)) descriptors = {81.header = {82.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),83.flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |84FUNCTIONFS_HAS_HS_DESC),85.length = cpu_to_le32(sizeof(descriptors)),86},87.fs_count = cpu_to_le32(3),88.fs_descs = {89.intf = {90.bLength = sizeof(descriptors.fs_descs.intf),91.bDescriptorType = USB_DT_INTERFACE,92.bNumEndpoints = 2,93.bInterfaceClass = USB_CLASS_VENDOR_SPEC,94.iInterface = 1,95},96.bulk_sink = {97.bLength = sizeof(descriptors.fs_descs.bulk_sink),98.bDescriptorType = USB_DT_ENDPOINT,99.bEndpointAddress = 1 | USB_DIR_IN,100.bmAttributes = USB_ENDPOINT_XFER_BULK,101},102.bulk_source = {103.bLength = sizeof(descriptors.fs_descs.bulk_source),104.bDescriptorType = USB_DT_ENDPOINT,105.bEndpointAddress = 2 | USB_DIR_OUT,106.bmAttributes = USB_ENDPOINT_XFER_BULK,107},108},109.hs_count = cpu_to_le32(3),110.hs_descs = {111.intf = {112.bLength = sizeof(descriptors.hs_descs.intf),113.bDescriptorType = USB_DT_INTERFACE,114.bNumEndpoints = 2,115.bInterfaceClass = USB_CLASS_VENDOR_SPEC,116.iInterface = 1,117},118.bulk_sink = {119.bLength = sizeof(descriptors.hs_descs.bulk_sink),120.bDescriptorType = USB_DT_ENDPOINT,121.bEndpointAddress = 1 | USB_DIR_IN,122.bmAttributes = USB_ENDPOINT_XFER_BULK,123.wMaxPacketSize = cpu_to_le16(512),124},125.bulk_source = {126.bLength = sizeof(descriptors.hs_descs.bulk_source),127.bDescriptorType = USB_DT_ENDPOINT,128.bEndpointAddress = 2 | USB_DIR_OUT,129.bmAttributes = USB_ENDPOINT_XFER_BULK,130.wMaxPacketSize = cpu_to_le16(512),131},132},133};134135#define STR_INTERFACE "AIO Test"136137static const struct {138struct usb_functionfs_strings_head header;139struct {140__le16 code;141const char str1[sizeof(STR_INTERFACE)];142} __attribute__ ((__packed__)) lang0;143} __attribute__ ((__packed__)) strings = {144.header = {145.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),146.length = cpu_to_le32(sizeof(strings)),147.str_count = cpu_to_le32(1),148.lang_count = cpu_to_le32(1),149},150.lang0 = {151cpu_to_le16(0x0409), /* en-us */152STR_INTERFACE,153},154};155156/******************** Endpoints handling *******************************/157158static void display_event(struct usb_functionfs_event *event)159{160static const char *const names[] = {161[FUNCTIONFS_BIND] = "BIND",162[FUNCTIONFS_UNBIND] = "UNBIND",163[FUNCTIONFS_ENABLE] = "ENABLE",164[FUNCTIONFS_DISABLE] = "DISABLE",165[FUNCTIONFS_SETUP] = "SETUP",166[FUNCTIONFS_SUSPEND] = "SUSPEND",167[FUNCTIONFS_RESUME] = "RESUME",168};169switch (event->type) {170case FUNCTIONFS_BIND:171case FUNCTIONFS_UNBIND:172case FUNCTIONFS_ENABLE:173case FUNCTIONFS_DISABLE:174case FUNCTIONFS_SETUP:175case FUNCTIONFS_SUSPEND:176case FUNCTIONFS_RESUME:177printf("Event %s\n", names[event->type]);178}179}180181static void handle_ep0(int ep0, bool *ready)182{183struct usb_functionfs_event event;184int ret;185186struct pollfd pfds[1];187pfds[0].fd = ep0;188pfds[0].events = POLLIN;189190ret = poll(pfds, 1, 0);191192if (ret && (pfds[0].revents & POLLIN)) {193ret = read(ep0, &event, sizeof(event));194if (!ret) {195perror("unable to read event from ep0");196return;197}198display_event(&event);199switch (event.type) {200case FUNCTIONFS_SETUP:201if (event.u.setup.bRequestType & USB_DIR_IN)202write(ep0, NULL, 0);203else204read(ep0, NULL, 0);205break;206207case FUNCTIONFS_ENABLE:208*ready = true;209break;210211case FUNCTIONFS_DISABLE:212*ready = false;213break;214215default:216break;217}218}219}220221int main(int argc, char *argv[])222{223int i, ret;224char *ep_path;225226int ep0;227int ep[2];228229io_context_t ctx;230231int evfd;232fd_set rfds;233234char *buf_in, *buf_out;235struct iocb *iocb_in, *iocb_out;236int req_in = 0, req_out = 0;237bool ready;238239if (argc != 2) {240printf("ffs directory not specified!\n");241return 1;242}243244ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);245if (!ep_path) {246perror("malloc");247return 1;248}249250/* open endpoint files */251sprintf(ep_path, "%s/ep0", argv[1]);252ep0 = open(ep_path, O_RDWR);253if (ep0 < 0) {254perror("unable to open ep0");255return 1;256}257if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {258perror("unable do write descriptors");259return 1;260}261if (write(ep0, &strings, sizeof(strings)) < 0) {262perror("unable to write strings");263return 1;264}265for (i = 0; i < 2; ++i) {266sprintf(ep_path, "%s/ep%d", argv[1], i+1);267ep[i] = open(ep_path, O_RDWR);268if (ep[i] < 0) {269printf("unable to open ep%d: %s\n", i+1,270strerror(errno));271return 1;272}273}274275free(ep_path);276277memset(&ctx, 0, sizeof(ctx));278/* setup aio context to handle up to 2 requests */279if (io_setup(2, &ctx) < 0) {280perror("unable to setup aio");281return 1;282}283284evfd = eventfd(0, 0);285if (evfd < 0) {286perror("unable to open eventfd");287return 1;288}289290/* alloc buffers and requests */291buf_in = malloc(BUF_LEN);292buf_out = malloc(BUF_LEN);293iocb_in = malloc(sizeof(*iocb_in));294iocb_out = malloc(sizeof(*iocb_out));295296while (1) {297FD_ZERO(&rfds);298FD_SET(ep0, &rfds);299FD_SET(evfd, &rfds);300301ret = select(((ep0 > evfd) ? ep0 : evfd)+1,302&rfds, NULL, NULL, NULL);303if (ret < 0) {304if (errno == EINTR)305continue;306perror("select");307break;308}309310if (FD_ISSET(ep0, &rfds))311handle_ep0(ep0, &ready);312313/* we are waiting for function ENABLE */314if (!ready)315continue;316317/* if something was submitted we wait for event */318if (FD_ISSET(evfd, &rfds)) {319uint64_t ev_cnt;320ret = read(evfd, &ev_cnt, sizeof(ev_cnt));321if (ret < 0) {322perror("unable to read eventfd");323break;324}325326struct io_event e[2];327/* we wait for one event */328ret = io_getevents(ctx, 1, 2, e, NULL);329/* if we got event */330for (i = 0; i < ret; ++i) {331if (e[i].obj->aio_fildes == ep[0]) {332printf("ev=in; ret=%lu\n", e[i].res);333req_in = 0;334} else if (e[i].obj->aio_fildes == ep[1]) {335printf("ev=out; ret=%lu\n", e[i].res);336req_out = 0;337}338}339}340341if (!req_in) { /* if IN transfer not requested*/342/* prepare write request */343io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);344/* enable eventfd notification */345iocb_in->u.c.flags |= IOCB_FLAG_RESFD;346iocb_in->u.c.resfd = evfd;347/* submit table of requests */348ret = io_submit(ctx, 1, &iocb_in);349if (ret >= 0) { /* if ret > 0 request is queued */350req_in = 1;351printf("submit: in\n");352} else353perror("unable to submit request");354}355if (!req_out) { /* if OUT transfer not requested */356/* prepare read request */357io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);358/* enable eventfs notification */359iocb_out->u.c.flags |= IOCB_FLAG_RESFD;360iocb_out->u.c.resfd = evfd;361/* submit table of requests */362ret = io_submit(ctx, 1, &iocb_out);363if (ret >= 0) { /* if ret > 0 request is queued */364req_out = 1;365printf("submit: out\n");366} else367perror("unable to submit request");368}369}370371/* free resources */372373io_destroy(ctx);374375free(buf_in);376free(buf_out);377free(iocb_in);378free(iocb_out);379380for (i = 0; i < 2; ++i)381close(ep[i]);382close(ep0);383384return 0;385}386387388