linux/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
<<
>>
Prefs
   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, or
   5 * distribute this software, either in source code form or as a compiled
   6 * binary, for any purpose, commercial or non-commercial, and by any
   7 * means.
   8 *
   9 * In jurisdictions that recognize copyright laws, the author or authors
  10 * of this software dedicate any and all copyright interest in the
  11 * software to the public domain. We make this dedication for the benefit
  12 * of the public at large and to the detriment of our heirs and
  13 * successors. We intend this dedication to be an overt act of
  14 * relinquishment in perpetuity of all present and future rights to this
  15 * 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 OF
  19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  21 * 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 OR
  23 * OTHER DEALINGS IN THE SOFTWARE.
  24 *
  25 * For more information, please refer to <http://unlicense.org/>
  26 */
  27
  28#define _BSD_SOURCE /* for endian.h */
  29
  30#include <endian.h>
  31#include <errno.h>
  32#include <fcntl.h>
  33#include <stdarg.h>
  34#include <stdio.h>
  35#include <stdlib.h>
  36#include <string.h>
  37#include <sys/ioctl.h>
  38#include <sys/stat.h>
  39#include <sys/types.h>
  40#include <sys/poll.h>
  41#include <unistd.h>
  42#include <stdbool.h>
  43#include <sys/eventfd.h>
  44
  45#include "libaio.h"
  46#define IOCB_FLAG_RESFD         (1 << 0)
  47
  48#include <linux/usb/functionfs.h>
  49
  50#define BUF_LEN         8192
  51
  52/******************** Descriptors and Strings *******************************/
  53
  54static const struct {
  55        struct usb_functionfs_descs_head_v2 header;
  56        __le32 fs_count;
  57        __le32 hs_count;
  58        struct {
  59                struct usb_interface_descriptor intf;
  60                struct usb_endpoint_descriptor_no_audio bulk_sink;
  61                struct usb_endpoint_descriptor_no_audio bulk_source;
  62        } __attribute__ ((__packed__)) fs_descs, hs_descs;
  63} __attribute__ ((__packed__)) descriptors = {
  64        .header = {
  65                .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
  66                .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
  67                                     FUNCTIONFS_HAS_HS_DESC),
  68                .length = htole32(sizeof(descriptors)),
  69        },
  70        .fs_count = htole32(3),
  71        .fs_descs = {
  72                .intf = {
  73                        .bLength = sizeof(descriptors.fs_descs.intf),
  74                        .bDescriptorType = USB_DT_INTERFACE,
  75                        .bNumEndpoints = 2,
  76                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
  77                        .iInterface = 1,
  78                },
  79                .bulk_sink = {
  80                        .bLength = sizeof(descriptors.fs_descs.bulk_sink),
  81                        .bDescriptorType = USB_DT_ENDPOINT,
  82                        .bEndpointAddress = 1 | USB_DIR_IN,
  83                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
  84                },
  85                .bulk_source = {
  86                        .bLength = sizeof(descriptors.fs_descs.bulk_source),
  87                        .bDescriptorType = USB_DT_ENDPOINT,
  88                        .bEndpointAddress = 2 | USB_DIR_OUT,
  89                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
  90                },
  91        },
  92        .hs_count = htole32(3),
  93        .hs_descs = {
  94                .intf = {
  95                        .bLength = sizeof(descriptors.hs_descs.intf),
  96                        .bDescriptorType = USB_DT_INTERFACE,
  97                        .bNumEndpoints = 2,
  98                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
  99                        .iInterface = 1,
 100                },
 101                .bulk_sink = {
 102                        .bLength = sizeof(descriptors.hs_descs.bulk_sink),
 103                        .bDescriptorType = USB_DT_ENDPOINT,
 104                        .bEndpointAddress = 1 | USB_DIR_IN,
 105                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 106                        .wMaxPacketSize = htole16(512),
 107                },
 108                .bulk_source = {
 109                        .bLength = sizeof(descriptors.hs_descs.bulk_source),
 110                        .bDescriptorType = USB_DT_ENDPOINT,
 111                        .bEndpointAddress = 2 | USB_DIR_OUT,
 112                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 113                        .wMaxPacketSize = htole16(512),
 114                },
 115        },
 116};
 117
 118#define STR_INTERFACE "AIO Test"
 119
 120static const struct {
 121        struct usb_functionfs_strings_head header;
 122        struct {
 123                __le16 code;
 124                const char str1[sizeof(STR_INTERFACE)];
 125        } __attribute__ ((__packed__)) lang0;
 126} __attribute__ ((__packed__)) strings = {
 127        .header = {
 128                .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
 129                .length = htole32(sizeof(strings)),
 130                .str_count = htole32(1),
 131                .lang_count = htole32(1),
 132        },
 133        .lang0 = {
 134                htole16(0x0409), /* en-us */
 135                STR_INTERFACE,
 136        },
 137};
 138
 139/******************** Endpoints handling *******************************/
 140
 141static void display_event(struct usb_functionfs_event *event)
 142{
 143        static const char *const names[] = {
 144                [FUNCTIONFS_BIND] = "BIND",
 145                [FUNCTIONFS_UNBIND] = "UNBIND",
 146                [FUNCTIONFS_ENABLE] = "ENABLE",
 147                [FUNCTIONFS_DISABLE] = "DISABLE",
 148                [FUNCTIONFS_SETUP] = "SETUP",
 149                [FUNCTIONFS_SUSPEND] = "SUSPEND",
 150                [FUNCTIONFS_RESUME] = "RESUME",
 151        };
 152        switch (event->type) {
 153        case FUNCTIONFS_BIND:
 154        case FUNCTIONFS_UNBIND:
 155        case FUNCTIONFS_ENABLE:
 156        case FUNCTIONFS_DISABLE:
 157        case FUNCTIONFS_SETUP:
 158        case FUNCTIONFS_SUSPEND:
 159        case FUNCTIONFS_RESUME:
 160                printf("Event %s\n", names[event->type]);
 161        }
 162}
 163
 164static void handle_ep0(int ep0, bool *ready)
 165{
 166        struct usb_functionfs_event event;
 167        int ret;
 168
 169        struct pollfd pfds[1];
 170        pfds[0].fd = ep0;
 171        pfds[0].events = POLLIN;
 172
 173        ret = poll(pfds, 1, 0);
 174
 175        if (ret && (pfds[0].revents & POLLIN)) {
 176                ret = read(ep0, &event, sizeof(event));
 177                if (!ret) {
 178                        perror("unable to read event from ep0");
 179                        return;
 180                }
 181                display_event(&event);
 182                switch (event.type) {
 183                case FUNCTIONFS_SETUP:
 184                        if (event.u.setup.bRequestType & USB_DIR_IN)
 185                                write(ep0, NULL, 0);
 186                        else
 187                                read(ep0, NULL, 0);
 188                        break;
 189
 190                case FUNCTIONFS_ENABLE:
 191                        *ready = true;
 192                        break;
 193
 194                case FUNCTIONFS_DISABLE:
 195                        *ready = false;
 196                        break;
 197
 198                default:
 199                        break;
 200                }
 201        }
 202}
 203
 204int main(int argc, char *argv[])
 205{
 206        int i, ret;
 207        char *ep_path;
 208
 209        int ep0;
 210        int ep[2];
 211
 212        io_context_t ctx;
 213
 214        int evfd;
 215        fd_set rfds;
 216
 217        char *buf_in, *buf_out;
 218        struct iocb *iocb_in, *iocb_out;
 219        int req_in = 0, req_out = 0;
 220        bool ready;
 221
 222        if (argc != 2) {
 223                printf("ffs directory not specified!\n");
 224                return 1;
 225        }
 226
 227        ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
 228        if (!ep_path) {
 229                perror("malloc");
 230                return 1;
 231        }
 232
 233        /* open endpoint files */
 234        sprintf(ep_path, "%s/ep0", argv[1]);
 235        ep0 = open(ep_path, O_RDWR);
 236        if (ep0 < 0) {
 237                perror("unable to open ep0");
 238                return 1;
 239        }
 240        if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
 241                perror("unable do write descriptors");
 242                return 1;
 243        }
 244        if (write(ep0, &strings, sizeof(strings)) < 0) {
 245                perror("unable to write strings");
 246                return 1;
 247        }
 248        for (i = 0; i < 2; ++i) {
 249                sprintf(ep_path, "%s/ep%d", argv[1], i+1);
 250                ep[i] = open(ep_path, O_RDWR);
 251                if (ep[i] < 0) {
 252                        printf("unable to open ep%d: %s\n", i+1,
 253                               strerror(errno));
 254                        return 1;
 255                }
 256        }
 257
 258        free(ep_path);
 259
 260        memset(&ctx, 0, sizeof(ctx));
 261        /* setup aio context to handle up to 2 requests */
 262        if (io_setup(2, &ctx) < 0) {
 263                perror("unable to setup aio");
 264                return 1;
 265        }
 266
 267        evfd = eventfd(0, 0);
 268        if (evfd < 0) {
 269                perror("unable to open eventfd");
 270                return 1;
 271        }
 272
 273        /* alloc buffers and requests */
 274        buf_in = malloc(BUF_LEN);
 275        buf_out = malloc(BUF_LEN);
 276        iocb_in = malloc(sizeof(*iocb_in));
 277        iocb_out = malloc(sizeof(*iocb_out));
 278
 279        while (1) {
 280                FD_ZERO(&rfds);
 281                FD_SET(ep0, &rfds);
 282                FD_SET(evfd, &rfds);
 283
 284                ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
 285                             &rfds, NULL, NULL, NULL);
 286                if (ret < 0) {
 287                        if (errno == EINTR)
 288                                continue;
 289                        perror("select");
 290                        break;
 291                }
 292
 293                if (FD_ISSET(ep0, &rfds))
 294                        handle_ep0(ep0, &ready);
 295
 296                /* we are waiting for function ENABLE */
 297                if (!ready)
 298                        continue;
 299
 300                /* if something was submitted we wait for event */
 301                if (FD_ISSET(evfd, &rfds)) {
 302                        uint64_t ev_cnt;
 303                        ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
 304                        if (ret < 0) {
 305                                perror("unable to read eventfd");
 306                                break;
 307                        }
 308
 309                        struct io_event e[2];
 310                        /* we wait for one event */
 311                        ret = io_getevents(ctx, 1, 2, e, NULL);
 312                        /* if we got event */
 313                        for (i = 0; i < ret; ++i) {
 314                                if (e[i].obj->aio_fildes == ep[0]) {
 315                                        printf("ev=in; ret=%lu\n", e[i].res);
 316                                        req_in = 0;
 317                                } else if (e[i].obj->aio_fildes == ep[1]) {
 318                                        printf("ev=out; ret=%lu\n", e[i].res);
 319                                        req_out = 0;
 320                                }
 321                        }
 322                }
 323
 324                if (!req_in) { /* if IN transfer not requested*/
 325                        /* prepare write request */
 326                        io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
 327                        /* enable eventfd notification */
 328                        iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
 329                        iocb_in->u.c.resfd = evfd;
 330                        /* submit table of requests */
 331                        ret = io_submit(ctx, 1, &iocb_in);
 332                        if (ret >= 0) { /* if ret > 0 request is queued */
 333                                req_in = 1;
 334                                printf("submit: in\n");
 335                        } else
 336                                perror("unable to submit request");
 337                }
 338                if (!req_out) { /* if OUT transfer not requested */
 339                        /* prepare read request */
 340                        io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
 341                        /* enable eventfs notification */
 342                        iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
 343                        iocb_out->u.c.resfd = evfd;
 344                        /* submit table of requests */
 345                        ret = io_submit(ctx, 1, &iocb_out);
 346                        if (ret >= 0) { /* if ret > 0 request is queued */
 347                                req_out = 1;
 348                                printf("submit: out\n");
 349                        } else
 350                                perror("unable to submit request");
 351                }
 352        }
 353
 354        /* free resources */
 355
 356        io_destroy(ctx);
 357
 358        free(buf_in);
 359        free(buf_out);
 360        free(iocb_in);
 361        free(iocb_out);
 362
 363        for (i = 0; i < 2; ++i)
 364                close(ep[i]);
 365        close(ep0);
 366
 367        return 0;
 368}
 369