linux/tools/usb/ffs-test.c
<<
>>
Prefs
   1/*
   2 * ffs-test.c.c -- user mode filesystem api for usb composite function
   3 *
   4 * Copyright (C) 2010 Samsung Electronics
   5 *                    Author: Michal Nazarewicz <mina86@mina86.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22/* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
  23
  24
  25#define _BSD_SOURCE /* for endian.h */
  26
  27#include <endian.h>
  28#include <errno.h>
  29#include <fcntl.h>
  30#include <pthread.h>
  31#include <stdarg.h>
  32#include <stdio.h>
  33#include <stdlib.h>
  34#include <string.h>
  35#include <sys/ioctl.h>
  36#include <sys/stat.h>
  37#include <sys/types.h>
  38#include <unistd.h>
  39#include <tools/le_byteshift.h>
  40
  41#include "../../include/linux/usb/functionfs.h"
  42
  43
  44/******************** Little Endian Handling ********************************/
  45
  46#define cpu_to_le16(x)  htole16(x)
  47#define cpu_to_le32(x)  htole32(x)
  48#define le32_to_cpu(x)  le32toh(x)
  49#define le16_to_cpu(x)  le16toh(x)
  50
  51
  52/******************** Messages and Errors ***********************************/
  53
  54static const char argv0[] = "ffs-test";
  55
  56static unsigned verbosity = 7;
  57
  58static void _msg(unsigned level, const char *fmt, ...)
  59{
  60        if (level < 2)
  61                level = 2;
  62        else if (level > 7)
  63                level = 7;
  64
  65        if (level <= verbosity) {
  66                static const char levels[8][6] = {
  67                        [2] = "crit:",
  68                        [3] = "err: ",
  69                        [4] = "warn:",
  70                        [5] = "note:",
  71                        [6] = "info:",
  72                        [7] = "dbg: "
  73                };
  74
  75                int _errno = errno;
  76                va_list ap;
  77
  78                fprintf(stderr, "%s: %s ", argv0, levels[level]);
  79                va_start(ap, fmt);
  80                vfprintf(stderr, fmt, ap);
  81                va_end(ap);
  82
  83                if (fmt[strlen(fmt) - 1] != '\n') {
  84                        char buffer[128];
  85                        strerror_r(_errno, buffer, sizeof buffer);
  86                        fprintf(stderr, ": (-%d) %s\n", _errno, buffer);
  87                }
  88
  89                fflush(stderr);
  90        }
  91}
  92
  93#define die(...)  (_msg(2, __VA_ARGS__), exit(1))
  94#define err(...)   _msg(3, __VA_ARGS__)
  95#define warn(...)  _msg(4, __VA_ARGS__)
  96#define note(...)  _msg(5, __VA_ARGS__)
  97#define info(...)  _msg(6, __VA_ARGS__)
  98#define debug(...) _msg(7, __VA_ARGS__)
  99
 100#define die_on(cond, ...) do { \
 101        if (cond) \
 102                die(__VA_ARGS__); \
 103        } while (0)
 104
 105
 106/******************** Descriptors and Strings *******************************/
 107
 108static const struct {
 109        struct usb_functionfs_descs_head header;
 110        struct {
 111                struct usb_interface_descriptor intf;
 112                struct usb_endpoint_descriptor_no_audio sink;
 113                struct usb_endpoint_descriptor_no_audio source;
 114        } __attribute__((packed)) fs_descs, hs_descs;
 115} __attribute__((packed)) descriptors = {
 116        .header = {
 117                .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
 118                .length = cpu_to_le32(sizeof descriptors),
 119                .fs_count = 3,
 120                .hs_count = 3,
 121        },
 122        .fs_descs = {
 123                .intf = {
 124                        .bLength = sizeof descriptors.fs_descs.intf,
 125                        .bDescriptorType = USB_DT_INTERFACE,
 126                        .bNumEndpoints = 2,
 127                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
 128                        .iInterface = 1,
 129                },
 130                .sink = {
 131                        .bLength = sizeof descriptors.fs_descs.sink,
 132                        .bDescriptorType = USB_DT_ENDPOINT,
 133                        .bEndpointAddress = 1 | USB_DIR_IN,
 134                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 135                        /* .wMaxPacketSize = autoconfiguration (kernel) */
 136                },
 137                .source = {
 138                        .bLength = sizeof descriptors.fs_descs.source,
 139                        .bDescriptorType = USB_DT_ENDPOINT,
 140                        .bEndpointAddress = 2 | USB_DIR_OUT,
 141                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 142                        /* .wMaxPacketSize = autoconfiguration (kernel) */
 143                },
 144        },
 145        .hs_descs = {
 146                .intf = {
 147                        .bLength = sizeof descriptors.fs_descs.intf,
 148                        .bDescriptorType = USB_DT_INTERFACE,
 149                        .bNumEndpoints = 2,
 150                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
 151                        .iInterface = 1,
 152                },
 153                .sink = {
 154                        .bLength = sizeof descriptors.hs_descs.sink,
 155                        .bDescriptorType = USB_DT_ENDPOINT,
 156                        .bEndpointAddress = 1 | USB_DIR_IN,
 157                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 158                        .wMaxPacketSize = cpu_to_le16(512),
 159                },
 160                .source = {
 161                        .bLength = sizeof descriptors.hs_descs.source,
 162                        .bDescriptorType = USB_DT_ENDPOINT,
 163                        .bEndpointAddress = 2 | USB_DIR_OUT,
 164                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
 165                        .wMaxPacketSize = cpu_to_le16(512),
 166                        .bInterval = 1, /* NAK every 1 uframe */
 167                },
 168        },
 169};
 170
 171
 172#define STR_INTERFACE_ "Source/Sink"
 173
 174static const struct {
 175        struct usb_functionfs_strings_head header;
 176        struct {
 177                __le16 code;
 178                const char str1[sizeof STR_INTERFACE_];
 179        } __attribute__((packed)) lang0;
 180} __attribute__((packed)) strings = {
 181        .header = {
 182                .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
 183                .length = cpu_to_le32(sizeof strings),
 184                .str_count = cpu_to_le32(1),
 185                .lang_count = cpu_to_le32(1),
 186        },
 187        .lang0 = {
 188                cpu_to_le16(0x0409), /* en-us */
 189                STR_INTERFACE_,
 190        },
 191};
 192
 193#define STR_INTERFACE strings.lang0.str1
 194
 195
 196/******************** Files and Threads Handling ****************************/
 197
 198struct thread;
 199
 200static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
 201static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
 202static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
 203static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
 204static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
 205
 206
 207static struct thread {
 208        const char *const filename;
 209        size_t buf_size;
 210
 211        ssize_t (*in)(struct thread *, void *, size_t);
 212        const char *const in_name;
 213
 214        ssize_t (*out)(struct thread *, const void *, size_t);
 215        const char *const out_name;
 216
 217        int fd;
 218        pthread_t id;
 219        void *buf;
 220        ssize_t status;
 221} threads[] = {
 222        {
 223                "ep0", 4 * sizeof(struct usb_functionfs_event),
 224                read_wrap, NULL,
 225                ep0_consume, "<consume>",
 226                0, 0, NULL, 0
 227        },
 228        {
 229                "ep1", 8 * 1024,
 230                fill_in_buf, "<in>",
 231                write_wrap, NULL,
 232                0, 0, NULL, 0
 233        },
 234        {
 235                "ep2", 8 * 1024,
 236                read_wrap, NULL,
 237                empty_out_buf, "<out>",
 238                0, 0, NULL, 0
 239        },
 240};
 241
 242
 243static void init_thread(struct thread *t)
 244{
 245        t->buf = malloc(t->buf_size);
 246        die_on(!t->buf, "malloc");
 247
 248        t->fd = open(t->filename, O_RDWR);
 249        die_on(t->fd < 0, "%s", t->filename);
 250}
 251
 252static void cleanup_thread(void *arg)
 253{
 254        struct thread *t = arg;
 255        int ret, fd;
 256
 257        fd = t->fd;
 258        if (t->fd < 0)
 259                return;
 260        t->fd = -1;
 261
 262        /* test the FIFO ioctls (non-ep0 code paths) */
 263        if (t != threads) {
 264                ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS);
 265                if (ret < 0) {
 266                        /* ENODEV reported after disconnect */
 267                        if (errno != ENODEV)
 268                                err("%s: get fifo status", t->filename);
 269                } else if (ret) {
 270                        warn("%s: unclaimed = %d\n", t->filename, ret);
 271                        if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0)
 272                                err("%s: fifo flush", t->filename);
 273                }
 274        }
 275
 276        if (close(fd) < 0)
 277                err("%s: close", t->filename);
 278
 279        free(t->buf);
 280        t->buf = NULL;
 281}
 282
 283static void *start_thread_helper(void *arg)
 284{
 285        const char *name, *op, *in_name, *out_name;
 286        struct thread *t = arg;
 287        ssize_t ret;
 288
 289        info("%s: starts\n", t->filename);
 290        in_name = t->in_name ? t->in_name : t->filename;
 291        out_name = t->out_name ? t->out_name : t->filename;
 292
 293        pthread_cleanup_push(cleanup_thread, arg);
 294
 295        for (;;) {
 296                pthread_testcancel();
 297
 298                ret = t->in(t, t->buf, t->buf_size);
 299                if (ret > 0) {
 300                        ret = t->out(t, t->buf, ret);
 301                        name = out_name;
 302                        op = "write";
 303                } else {
 304                        name = in_name;
 305                        op = "read";
 306                }
 307
 308                if (ret > 0) {
 309                        /* nop */
 310                } else if (!ret) {
 311                        debug("%s: %s: EOF", name, op);
 312                        break;
 313                } else if (errno == EINTR || errno == EAGAIN) {
 314                        debug("%s: %s", name, op);
 315                } else {
 316                        warn("%s: %s", name, op);
 317                        break;
 318                }
 319        }
 320
 321        pthread_cleanup_pop(1);
 322
 323        t->status = ret;
 324        info("%s: ends\n", t->filename);
 325        return NULL;
 326}
 327
 328static void start_thread(struct thread *t)
 329{
 330        debug("%s: starting\n", t->filename);
 331
 332        die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0,
 333               "pthread_create(%s)", t->filename);
 334}
 335
 336static void join_thread(struct thread *t)
 337{
 338        int ret = pthread_join(t->id, NULL);
 339
 340        if (ret < 0)
 341                err("%s: joining thread", t->filename);
 342        else
 343                debug("%s: joined\n", t->filename);
 344}
 345
 346
 347static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
 348{
 349        return read(t->fd, buf, nbytes);
 350}
 351
 352static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes)
 353{
 354        return write(t->fd, buf, nbytes);
 355}
 356
 357
 358/******************** Empty/Fill buffer routines ****************************/
 359
 360/* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */
 361enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
 362static enum pattern pattern;
 363
 364static ssize_t
 365fill_in_buf(struct thread *ignore, void *buf, size_t nbytes)
 366{
 367        size_t i;
 368        __u8 *p;
 369
 370        (void)ignore;
 371
 372        switch (pattern) {
 373        case PAT_ZERO:
 374                memset(buf, 0, nbytes);
 375                break;
 376
 377        case PAT_SEQ:
 378                for (p = buf, i = 0; i < nbytes; ++i, ++p)
 379                        *p = i % 63;
 380                break;
 381
 382        case PAT_PIPE:
 383                return fread(buf, 1, nbytes, stdin);
 384        }
 385
 386        return nbytes;
 387}
 388
 389static ssize_t
 390empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
 391{
 392        const __u8 *p;
 393        __u8 expected;
 394        ssize_t ret;
 395        size_t len;
 396
 397        (void)ignore;
 398
 399        switch (pattern) {
 400        case PAT_ZERO:
 401                expected = 0;
 402                for (p = buf, len = 0; len < nbytes; ++p, ++len)
 403                        if (*p)
 404                                goto invalid;
 405                break;
 406
 407        case PAT_SEQ:
 408                for (p = buf, len = 0; len < nbytes; ++p, ++len)
 409                        if (*p != len % 63) {
 410                                expected = len % 63;
 411                                goto invalid;
 412                        }
 413                break;
 414
 415        case PAT_PIPE:
 416                ret = fwrite(buf, nbytes, 1, stdout);
 417                if (ret > 0)
 418                        fflush(stdout);
 419                break;
 420
 421invalid:
 422                err("bad OUT byte %zd, expected %02x got %02x\n",
 423                    len, expected, *p);
 424                for (p = buf, len = 0; len < nbytes; ++p, ++len) {
 425                        if (0 == (len % 32))
 426                                fprintf(stderr, "%4zd:", len);
 427                        fprintf(stderr, " %02x", *p);
 428                        if (31 == (len % 32))
 429                                fprintf(stderr, "\n");
 430                }
 431                fflush(stderr);
 432                errno = EILSEQ;
 433                return -1;
 434        }
 435
 436        return len;
 437}
 438
 439
 440/******************** Endpoints routines ************************************/
 441
 442static void handle_setup(const struct usb_ctrlrequest *setup)
 443{
 444        printf("bRequestType = %d\n", setup->bRequestType);
 445        printf("bRequest     = %d\n", setup->bRequest);
 446        printf("wValue       = %d\n", le16_to_cpu(setup->wValue));
 447        printf("wIndex       = %d\n", le16_to_cpu(setup->wIndex));
 448        printf("wLength      = %d\n", le16_to_cpu(setup->wLength));
 449}
 450
 451static ssize_t
 452ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
 453{
 454        static const char *const names[] = {
 455                [FUNCTIONFS_BIND] = "BIND",
 456                [FUNCTIONFS_UNBIND] = "UNBIND",
 457                [FUNCTIONFS_ENABLE] = "ENABLE",
 458                [FUNCTIONFS_DISABLE] = "DISABLE",
 459                [FUNCTIONFS_SETUP] = "SETUP",
 460                [FUNCTIONFS_SUSPEND] = "SUSPEND",
 461                [FUNCTIONFS_RESUME] = "RESUME",
 462        };
 463
 464        const struct usb_functionfs_event *event = buf;
 465        size_t n;
 466
 467        (void)ignore;
 468
 469        for (n = nbytes / sizeof *event; n; --n, ++event)
 470                switch (event->type) {
 471                case FUNCTIONFS_BIND:
 472                case FUNCTIONFS_UNBIND:
 473                case FUNCTIONFS_ENABLE:
 474                case FUNCTIONFS_DISABLE:
 475                case FUNCTIONFS_SETUP:
 476                case FUNCTIONFS_SUSPEND:
 477                case FUNCTIONFS_RESUME:
 478                        printf("Event %s\n", names[event->type]);
 479                        if (event->type == FUNCTIONFS_SETUP)
 480                                handle_setup(&event->u.setup);
 481                        break;
 482
 483                default:
 484                        printf("Event %03u (unknown)\n", event->type);
 485                }
 486
 487        return nbytes;
 488}
 489
 490static void ep0_init(struct thread *t)
 491{
 492        ssize_t ret;
 493
 494        info("%s: writing descriptors\n", t->filename);
 495        ret = write(t->fd, &descriptors, sizeof descriptors);
 496        die_on(ret < 0, "%s: write: descriptors", t->filename);
 497
 498        info("%s: writing strings\n", t->filename);
 499        ret = write(t->fd, &strings, sizeof strings);
 500        die_on(ret < 0, "%s: write: strings", t->filename);
 501}
 502
 503
 504/******************** Main **************************************************/
 505
 506int main(void)
 507{
 508        unsigned i;
 509
 510        /* XXX TODO: Argument parsing missing */
 511
 512        init_thread(threads);
 513        ep0_init(threads);
 514
 515        for (i = 1; i < sizeof threads / sizeof *threads; ++i)
 516                init_thread(threads + i);
 517
 518        for (i = 1; i < sizeof threads / sizeof *threads; ++i)
 519                start_thread(threads + i);
 520
 521        start_thread_helper(threads);
 522
 523        for (i = 1; i < sizeof threads / sizeof *threads; ++i)
 524                join_thread(threads + i);
 525
 526        return 0;
 527}
 528
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.