The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/tools/usb/ffs-test.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   54 static const char argv0[] = "ffs-test";
   55 
   56 static unsigned verbosity = 7;
   57 
   58 static 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 
  108 static 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 
  174 static 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 
  198 struct thread;
  199 
  200 static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
  201 static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
  202 static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
  203 static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
  204 static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
  205 
  206 
  207 static 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 
  243 static 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 
  252 static 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 
  283 static 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 
  328 static 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 
  336 static 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 
  347 static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
  348 {
  349         return read(t->fd, buf, nbytes);
  350 }
  351 
  352 static 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 */
  361 enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
  362 static enum pattern pattern;
  363 
  364 static ssize_t
  365 fill_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 
  389 static ssize_t
  390 empty_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 
  421 invalid:
  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 
  442 static 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 
  451 static ssize_t
  452 ep0_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 
  490 static 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 
  506 int 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 }

Cache object: 60115061aa9e07760ac3e1cec1e2fefc


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.