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