linux/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.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#define BUFS_MAX        128
  52#define AIO_MAX         (BUFS_MAX*2)
  53
  54/******************** Descriptors and Strings *******************************/
  55
  56static const struct {
  57        struct usb_functionfs_descs_head_v2 header;
  58        __le32 fs_count;
  59        __le32 hs_count;
  60        struct {
  61                struct usb_interface_descriptor intf;
  62                struct usb_endpoint_descriptor_no_audio bulk_sink;
  63                struct usb_endpoint_descriptor_no_audio bulk_source;
  64        } __attribute__ ((__packed__)) fs_descs, hs_descs;
  65} __attribute__ ((__packed__)) descriptors = {
  66        .header = {
  67                .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
  68                .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
  69                                     FUNCTIONFS_HAS_HS_DESC),
  70                .length = htole32(sizeof(descriptors)),
  71        },
  72        .fs_count = htole32(3),
  73        .fs_descs = {
  74                .intf = {
  75                        .bLength = sizeof(descriptors.fs_descs.intf),
  76                        .bDescriptorType = USB_DT_INTERFACE,
  77                        .bNumEndpoints = 2,
  78                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
  79                        .iInterface = 1,
  80                },
  81                .bulk_sink = {
  82                        .bLength = sizeof(descriptors.fs_descs.bulk_sink),
  83                        .bDescriptorType = USB_DT_ENDPOINT,
  84                        .bEndpointAddress = 1 | USB_DIR_IN,
  85                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
  86                },
  87                .bulk_source = {
  88                        .bLength = sizeof(descriptors.fs_descs.bulk_source),
  89                        .bDescriptorType = USB_DT_ENDPOINT,
  90                        .bEndpointAddress = 2 | USB_DIR_OUT,
  91                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
  92                },
  93        },
  94        .hs_count = htole32(3),
  95        .hs_descs = {
  96                .intf = {
  97                        .bLength = sizeof(descriptors.hs_descs.intf),
  98                        .bDescriptorType = USB_DT_INTERFACE,
  99                        .bNumEndpoints = 2,
 100                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
 101                        .iInterface = 1,
 102                },
 103                .bulk_sink = {
 104                        .bLength = sizeof(descriptors.hs_descs.bulk_sink),
 105                        .bDescriptorType = USB_DT_ENDPOINT,
 106                        .bEndpointAddress = 1 | USB_DIR_IN,
 107                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 108                        .wMaxPacketSize = htole16(512),
 109                },
 110                .bulk_source = {
 111                        .bLength = sizeof(descriptors.hs_descs.bulk_source),
 112                        .bDescriptorType = USB_DT_ENDPOINT,
 113                        .bEndpointAddress = 2 | USB_DIR_OUT,
 114                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 115                        .wMaxPacketSize = htole16(512),
 116                },
 117        },
 118};
 119
 120#define STR_INTERFACE "AIO Test"
 121
 122static const struct {
 123        struct usb_functionfs_strings_head header;
 124        struct {
 125                __le16 code;
 126                const char str1[sizeof(STR_INTERFACE)];
 127        } __attribute__ ((__packed__)) lang0;
 128} __attribute__ ((__packed__)) strings = {
 129        .header = {
 130                .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
 131                .length = htole32(sizeof(strings)),
 132                .str_count = htole32(1),
 133                .lang_count = htole32(1),
 134        },
 135        .lang0 = {
 136                htole16(0x0409), /* en-us */
 137                STR_INTERFACE,
 138        },
 139};
 140
 141/********************** Buffer structure *******************************/
 142
 143struct io_buffer {
 144        struct iocb **iocb;
 145        unsigned char **buf;
 146        unsigned cnt;
 147        unsigned len;
 148        unsigned requested;
 149};
 150
 151/******************** Endpoints handling *******************************/
 152
 153static void display_event(struct usb_functionfs_event *event)
 154{
 155        static const char *const names[] = {
 156                [FUNCTIONFS_BIND] = "BIND",
 157                [FUNCTIONFS_UNBIND] = "UNBIND",
 158                [FUNCTIONFS_ENABLE] = "ENABLE",
 159                [FUNCTIONFS_DISABLE] = "DISABLE",
 160                [FUNCTIONFS_SETUP] = "SETUP",
 161                [FUNCTIONFS_SUSPEND] = "SUSPEND",
 162                [FUNCTIONFS_RESUME] = "RESUME",
 163        };
 164        switch (event->type) {
 165        case FUNCTIONFS_BIND:
 166        case FUNCTIONFS_UNBIND:
 167        case FUNCTIONFS_ENABLE:
 168        case FUNCTIONFS_DISABLE:
 169        case FUNCTIONFS_SETUP:
 170        case FUNCTIONFS_SUSPEND:
 171        case FUNCTIONFS_RESUME:
 172                printf("Event %s\n", names[event->type]);
 173        }
 174}
 175
 176static void handle_ep0(int ep0, bool *ready)
 177{
 178        int ret;
 179        struct usb_functionfs_event event;
 180
 181        ret = read(ep0, &event, sizeof(event));
 182        if (!ret) {
 183                perror("unable to read event from ep0");
 184                return;
 185        }
 186        display_event(&event);
 187        switch (event.type) {
 188        case FUNCTIONFS_SETUP:
 189                if (event.u.setup.bRequestType & USB_DIR_IN)
 190                        write(ep0, NULL, 0);
 191                else
 192                        read(ep0, NULL, 0);
 193                break;
 194
 195        case FUNCTIONFS_ENABLE:
 196                *ready = true;
 197                break;
 198
 199        case FUNCTIONFS_DISABLE:
 200                *ready = false;
 201                break;
 202
 203        default:
 204                break;
 205        }
 206}
 207
 208void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)
 209{
 210        unsigned i;
 211        iobuf->buf = malloc(n*sizeof(*iobuf->buf));
 212        iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
 213        iobuf->cnt = n;
 214        iobuf->len = len;
 215        iobuf->requested = 0;
 216        for (i = 0; i < n; ++i) {
 217                iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
 218                iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
 219        }
 220        iobuf->cnt = n;
 221}
 222
 223void delete_bufs(struct io_buffer *iobuf)
 224{
 225        unsigned i;
 226        for (i = 0; i < iobuf->cnt; ++i) {
 227                free(iobuf->buf[i]);
 228                free(iobuf->iocb[i]);
 229        }
 230        free(iobuf->buf);
 231        free(iobuf->iocb);
 232}
 233
 234int main(int argc, char *argv[])
 235{
 236        int ret;
 237        unsigned i, j;
 238        char *ep_path;
 239
 240        int ep0, ep1;
 241
 242        io_context_t ctx;
 243
 244        int evfd;
 245        fd_set rfds;
 246
 247        struct io_buffer iobuf[2];
 248        int actual = 0;
 249        bool ready;
 250
 251        if (argc != 2) {
 252                printf("ffs directory not specified!\n");
 253                return 1;
 254        }
 255
 256        ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
 257        if (!ep_path) {
 258                perror("malloc");
 259                return 1;
 260        }
 261
 262        /* open endpoint files */
 263        sprintf(ep_path, "%s/ep0", argv[1]);
 264        ep0 = open(ep_path, O_RDWR);
 265        if (ep0 < 0) {
 266                perror("unable to open ep0");
 267                return 1;
 268        }
 269        if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
 270                perror("unable do write descriptors");
 271                return 1;
 272        }
 273        if (write(ep0, &strings, sizeof(strings)) < 0) {
 274                perror("unable to write strings");
 275                return 1;
 276        }
 277        sprintf(ep_path, "%s/ep1", argv[1]);
 278        ep1 = open(ep_path, O_RDWR);
 279        if (ep1 < 0) {
 280                perror("unable to open ep1");
 281                return 1;
 282        }
 283
 284        free(ep_path);
 285
 286        memset(&ctx, 0, sizeof(ctx));
 287        /* setup aio context to handle up to AIO_MAX requests */
 288        if (io_setup(AIO_MAX, &ctx) < 0) {
 289                perror("unable to setup aio");
 290                return 1;
 291        }
 292
 293        evfd = eventfd(0, 0);
 294        if (evfd < 0) {
 295                perror("unable to open eventfd");
 296                return 1;
 297        }
 298
 299        for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
 300                init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);
 301
 302        while (1) {
 303                FD_ZERO(&rfds);
 304                FD_SET(ep0, &rfds);
 305                FD_SET(evfd, &rfds);
 306
 307                ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
 308                             &rfds, NULL, NULL, NULL);
 309                if (ret < 0) {
 310                        if (errno == EINTR)
 311                                continue;
 312                        perror("select");
 313                        break;
 314                }
 315
 316                if (FD_ISSET(ep0, &rfds))
 317                        handle_ep0(ep0, &ready);
 318
 319                /* we are waiting for function ENABLE */
 320                if (!ready)
 321                        continue;
 322
 323                /*
 324                 * when we're preparing new data to submit,
 325                 * second buffer being transmitted
 326                 */
 327                for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {
 328                        if (iobuf[i].requested)
 329                                continue;
 330                        /* prepare requests */
 331                        for (j = 0; j < iobuf[i].cnt; ++j) {
 332                                io_prep_pwrite(iobuf[i].iocb[j], ep1,
 333                                               iobuf[i].buf[j],
 334                                               iobuf[i].len, 0);
 335                                /* enable eventfd notification */
 336                                iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;
 337                                iobuf[i].iocb[j]->u.c.resfd = evfd;
 338                        }
 339                        /* submit table of requests */
 340                        ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);
 341                        if (ret >= 0) {
 342                                iobuf[i].requested = ret;
 343                                printf("submit: %d requests buf: %d\n", ret, i);
 344                        } else
 345                                perror("unable to submit requests");
 346                }
 347
 348                /* if event is ready to read */
 349                if (!FD_ISSET(evfd, &rfds))
 350                        continue;
 351
 352                uint64_t ev_cnt;
 353                ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
 354                if (ret < 0) {
 355                        perror("unable to read eventfd");
 356                        break;
 357                }
 358
 359                struct io_event e[BUFS_MAX];
 360                /* we read aio events */
 361                ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);
 362                if (ret > 0) /* if we got events */
 363                        iobuf[actual].requested -= ret;
 364
 365                /* if all req's from iocb completed */
 366                if (!iobuf[actual].requested)
 367                        actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));
 368        }
 369
 370        /* free resources */
 371
 372        for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
 373                delete_bufs(&iobuf[i]);
 374        io_destroy(ctx);
 375
 376        close(ep1);
 377        close(ep0);
 378
 379        return 0;
 380}
 381