193f2aa4dSMichal Nazarewicz /* 293f2aa4dSMichal Nazarewicz * ffs-test.c.c -- user mode filesystem api for usb composite function 393f2aa4dSMichal Nazarewicz * 493f2aa4dSMichal Nazarewicz * Copyright (C) 2010 Samsung Electronics 593f2aa4dSMichal Nazarewicz * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> 693f2aa4dSMichal Nazarewicz * 793f2aa4dSMichal Nazarewicz * This program is free software; you can redistribute it and/or modify 893f2aa4dSMichal Nazarewicz * it under the terms of the GNU General Public License as published by 993f2aa4dSMichal Nazarewicz * the Free Software Foundation; either version 2 of the License, or 1093f2aa4dSMichal Nazarewicz * (at your option) any later version. 1193f2aa4dSMichal Nazarewicz * 1293f2aa4dSMichal Nazarewicz * This program is distributed in the hope that it will be useful, 1393f2aa4dSMichal Nazarewicz * but WITHOUT ANY WARRANTY; without even the implied warranty of 1493f2aa4dSMichal Nazarewicz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1593f2aa4dSMichal Nazarewicz * GNU General Public License for more details. 1693f2aa4dSMichal Nazarewicz * 1793f2aa4dSMichal Nazarewicz * You should have received a copy of the GNU General Public License 1893f2aa4dSMichal Nazarewicz * along with this program; if not, write to the Free Software 1993f2aa4dSMichal Nazarewicz * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2093f2aa4dSMichal Nazarewicz */ 2193f2aa4dSMichal Nazarewicz 2293f2aa4dSMichal Nazarewicz /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */ 2393f2aa4dSMichal Nazarewicz 2493f2aa4dSMichal Nazarewicz 2593f2aa4dSMichal Nazarewicz #define _BSD_SOURCE /* for endian.h */ 2693f2aa4dSMichal Nazarewicz 2793f2aa4dSMichal Nazarewicz #include <endian.h> 2893f2aa4dSMichal Nazarewicz #include <errno.h> 2993f2aa4dSMichal Nazarewicz #include <fcntl.h> 3093f2aa4dSMichal Nazarewicz #include <pthread.h> 3193f2aa4dSMichal Nazarewicz #include <stdarg.h> 3293f2aa4dSMichal Nazarewicz #include <stdio.h> 3393f2aa4dSMichal Nazarewicz #include <stdlib.h> 3493f2aa4dSMichal Nazarewicz #include <string.h> 3593f2aa4dSMichal Nazarewicz #include <sys/ioctl.h> 3693f2aa4dSMichal Nazarewicz #include <sys/stat.h> 3793f2aa4dSMichal Nazarewicz #include <sys/types.h> 3893f2aa4dSMichal Nazarewicz #include <unistd.h> 39*24fa9a9dSMatt Fleming #include <tools/le_byteshift.h> 4093f2aa4dSMichal Nazarewicz 41d105e74eSDavidlohr Bueso #include "../../include/linux/usb/functionfs.h" 4293f2aa4dSMichal Nazarewicz 4393f2aa4dSMichal Nazarewicz 4493f2aa4dSMichal Nazarewicz /******************** Little Endian Handling ********************************/ 4593f2aa4dSMichal Nazarewicz 4693f2aa4dSMichal Nazarewicz #define cpu_to_le16(x) htole16(x) 4793f2aa4dSMichal Nazarewicz #define cpu_to_le32(x) htole32(x) 4893f2aa4dSMichal Nazarewicz #define le32_to_cpu(x) le32toh(x) 4993f2aa4dSMichal Nazarewicz #define le16_to_cpu(x) le16toh(x) 5093f2aa4dSMichal Nazarewicz 5193f2aa4dSMichal Nazarewicz 5293f2aa4dSMichal Nazarewicz /******************** Messages and Errors ***********************************/ 5393f2aa4dSMichal Nazarewicz 5493f2aa4dSMichal Nazarewicz static const char argv0[] = "ffs-test"; 5593f2aa4dSMichal Nazarewicz 5693f2aa4dSMichal Nazarewicz static unsigned verbosity = 7; 5793f2aa4dSMichal Nazarewicz 5893f2aa4dSMichal Nazarewicz static void _msg(unsigned level, const char *fmt, ...) 5993f2aa4dSMichal Nazarewicz { 6093f2aa4dSMichal Nazarewicz if (level < 2) 6193f2aa4dSMichal Nazarewicz level = 2; 6293f2aa4dSMichal Nazarewicz else if (level > 7) 6393f2aa4dSMichal Nazarewicz level = 7; 6493f2aa4dSMichal Nazarewicz 6593f2aa4dSMichal Nazarewicz if (level <= verbosity) { 6693f2aa4dSMichal Nazarewicz static const char levels[8][6] = { 6793f2aa4dSMichal Nazarewicz [2] = "crit:", 6893f2aa4dSMichal Nazarewicz [3] = "err: ", 6993f2aa4dSMichal Nazarewicz [4] = "warn:", 7093f2aa4dSMichal Nazarewicz [5] = "note:", 7193f2aa4dSMichal Nazarewicz [6] = "info:", 7293f2aa4dSMichal Nazarewicz [7] = "dbg: " 7393f2aa4dSMichal Nazarewicz }; 7493f2aa4dSMichal Nazarewicz 7593f2aa4dSMichal Nazarewicz int _errno = errno; 7693f2aa4dSMichal Nazarewicz va_list ap; 7793f2aa4dSMichal Nazarewicz 7893f2aa4dSMichal Nazarewicz fprintf(stderr, "%s: %s ", argv0, levels[level]); 7993f2aa4dSMichal Nazarewicz va_start(ap, fmt); 8093f2aa4dSMichal Nazarewicz vfprintf(stderr, fmt, ap); 8193f2aa4dSMichal Nazarewicz va_end(ap); 8293f2aa4dSMichal Nazarewicz 8393f2aa4dSMichal Nazarewicz if (fmt[strlen(fmt) - 1] != '\n') { 8493f2aa4dSMichal Nazarewicz char buffer[128]; 8593f2aa4dSMichal Nazarewicz strerror_r(_errno, buffer, sizeof buffer); 8693f2aa4dSMichal Nazarewicz fprintf(stderr, ": (-%d) %s\n", _errno, buffer); 8793f2aa4dSMichal Nazarewicz } 8893f2aa4dSMichal Nazarewicz 8993f2aa4dSMichal Nazarewicz fflush(stderr); 9093f2aa4dSMichal Nazarewicz } 9193f2aa4dSMichal Nazarewicz } 9293f2aa4dSMichal Nazarewicz 9393f2aa4dSMichal Nazarewicz #define die(...) (_msg(2, __VA_ARGS__), exit(1)) 9493f2aa4dSMichal Nazarewicz #define err(...) _msg(3, __VA_ARGS__) 9593f2aa4dSMichal Nazarewicz #define warn(...) _msg(4, __VA_ARGS__) 9693f2aa4dSMichal Nazarewicz #define note(...) _msg(5, __VA_ARGS__) 9793f2aa4dSMichal Nazarewicz #define info(...) _msg(6, __VA_ARGS__) 9893f2aa4dSMichal Nazarewicz #define debug(...) _msg(7, __VA_ARGS__) 9993f2aa4dSMichal Nazarewicz 10093f2aa4dSMichal Nazarewicz #define die_on(cond, ...) do { \ 10193f2aa4dSMichal Nazarewicz if (cond) \ 10293f2aa4dSMichal Nazarewicz die(__VA_ARGS__); \ 10393f2aa4dSMichal Nazarewicz } while (0) 10493f2aa4dSMichal Nazarewicz 10593f2aa4dSMichal Nazarewicz 10693f2aa4dSMichal Nazarewicz /******************** Descriptors and Strings *******************************/ 10793f2aa4dSMichal Nazarewicz 10893f2aa4dSMichal Nazarewicz static const struct { 10993f2aa4dSMichal Nazarewicz struct usb_functionfs_descs_head header; 11093f2aa4dSMichal Nazarewicz struct { 11193f2aa4dSMichal Nazarewicz struct usb_interface_descriptor intf; 11293f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio sink; 11393f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio source; 11493f2aa4dSMichal Nazarewicz } __attribute__((packed)) fs_descs, hs_descs; 11593f2aa4dSMichal Nazarewicz } __attribute__((packed)) descriptors = { 11693f2aa4dSMichal Nazarewicz .header = { 11793f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), 11893f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof descriptors), 11993f2aa4dSMichal Nazarewicz .fs_count = 3, 12093f2aa4dSMichal Nazarewicz .hs_count = 3, 12193f2aa4dSMichal Nazarewicz }, 12293f2aa4dSMichal Nazarewicz .fs_descs = { 12393f2aa4dSMichal Nazarewicz .intf = { 12493f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 12593f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 12693f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 12793f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 12893f2aa4dSMichal Nazarewicz .iInterface = 1, 12993f2aa4dSMichal Nazarewicz }, 13093f2aa4dSMichal Nazarewicz .sink = { 13193f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.sink, 13293f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 13393f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 13493f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 13593f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 13693f2aa4dSMichal Nazarewicz }, 13793f2aa4dSMichal Nazarewicz .source = { 13893f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.source, 13993f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 14093f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 14193f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 14293f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 14393f2aa4dSMichal Nazarewicz }, 14493f2aa4dSMichal Nazarewicz }, 14593f2aa4dSMichal Nazarewicz .hs_descs = { 14693f2aa4dSMichal Nazarewicz .intf = { 14793f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 14893f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 14993f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 15093f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 15193f2aa4dSMichal Nazarewicz .iInterface = 1, 15293f2aa4dSMichal Nazarewicz }, 15393f2aa4dSMichal Nazarewicz .sink = { 15493f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.sink, 15593f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 15693f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 15793f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 15893f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512), 15993f2aa4dSMichal Nazarewicz }, 16093f2aa4dSMichal Nazarewicz .source = { 16193f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.source, 16293f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 16393f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 16493f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 16593f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512), 16693f2aa4dSMichal Nazarewicz .bInterval = 1, /* NAK every 1 uframe */ 16793f2aa4dSMichal Nazarewicz }, 16893f2aa4dSMichal Nazarewicz }, 16993f2aa4dSMichal Nazarewicz }; 17093f2aa4dSMichal Nazarewicz 17193f2aa4dSMichal Nazarewicz 17293f2aa4dSMichal Nazarewicz #define STR_INTERFACE_ "Source/Sink" 17393f2aa4dSMichal Nazarewicz 17493f2aa4dSMichal Nazarewicz static const struct { 17593f2aa4dSMichal Nazarewicz struct usb_functionfs_strings_head header; 17693f2aa4dSMichal Nazarewicz struct { 17793f2aa4dSMichal Nazarewicz __le16 code; 17893f2aa4dSMichal Nazarewicz const char str1[sizeof STR_INTERFACE_]; 17993f2aa4dSMichal Nazarewicz } __attribute__((packed)) lang0; 18093f2aa4dSMichal Nazarewicz } __attribute__((packed)) strings = { 18193f2aa4dSMichal Nazarewicz .header = { 18293f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), 18393f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof strings), 18493f2aa4dSMichal Nazarewicz .str_count = cpu_to_le32(1), 18593f2aa4dSMichal Nazarewicz .lang_count = cpu_to_le32(1), 18693f2aa4dSMichal Nazarewicz }, 18793f2aa4dSMichal Nazarewicz .lang0 = { 18893f2aa4dSMichal Nazarewicz cpu_to_le16(0x0409), /* en-us */ 18993f2aa4dSMichal Nazarewicz STR_INTERFACE_, 19093f2aa4dSMichal Nazarewicz }, 19193f2aa4dSMichal Nazarewicz }; 19293f2aa4dSMichal Nazarewicz 19393f2aa4dSMichal Nazarewicz #define STR_INTERFACE strings.lang0.str1 19493f2aa4dSMichal Nazarewicz 19593f2aa4dSMichal Nazarewicz 19693f2aa4dSMichal Nazarewicz /******************** Files and Threads Handling ****************************/ 19793f2aa4dSMichal Nazarewicz 19893f2aa4dSMichal Nazarewicz struct thread; 19993f2aa4dSMichal Nazarewicz 20093f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes); 20193f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes); 20293f2aa4dSMichal Nazarewicz static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes); 20393f2aa4dSMichal Nazarewicz static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes); 20493f2aa4dSMichal Nazarewicz static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes); 20593f2aa4dSMichal Nazarewicz 20693f2aa4dSMichal Nazarewicz 20793f2aa4dSMichal Nazarewicz static struct thread { 20893f2aa4dSMichal Nazarewicz const char *const filename; 20993f2aa4dSMichal Nazarewicz size_t buf_size; 21093f2aa4dSMichal Nazarewicz 21193f2aa4dSMichal Nazarewicz ssize_t (*in)(struct thread *, void *, size_t); 21293f2aa4dSMichal Nazarewicz const char *const in_name; 21393f2aa4dSMichal Nazarewicz 21493f2aa4dSMichal Nazarewicz ssize_t (*out)(struct thread *, const void *, size_t); 21593f2aa4dSMichal Nazarewicz const char *const out_name; 21693f2aa4dSMichal Nazarewicz 21793f2aa4dSMichal Nazarewicz int fd; 21893f2aa4dSMichal Nazarewicz pthread_t id; 21993f2aa4dSMichal Nazarewicz void *buf; 22093f2aa4dSMichal Nazarewicz ssize_t status; 22193f2aa4dSMichal Nazarewicz } threads[] = { 22293f2aa4dSMichal Nazarewicz { 22393f2aa4dSMichal Nazarewicz "ep0", 4 * sizeof(struct usb_functionfs_event), 22493f2aa4dSMichal Nazarewicz read_wrap, NULL, 22593f2aa4dSMichal Nazarewicz ep0_consume, "<consume>", 22693f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 22793f2aa4dSMichal Nazarewicz }, 22893f2aa4dSMichal Nazarewicz { 22993f2aa4dSMichal Nazarewicz "ep1", 8 * 1024, 23093f2aa4dSMichal Nazarewicz fill_in_buf, "<in>", 23193f2aa4dSMichal Nazarewicz write_wrap, NULL, 23293f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 23393f2aa4dSMichal Nazarewicz }, 23493f2aa4dSMichal Nazarewicz { 23593f2aa4dSMichal Nazarewicz "ep2", 8 * 1024, 23693f2aa4dSMichal Nazarewicz read_wrap, NULL, 23793f2aa4dSMichal Nazarewicz empty_out_buf, "<out>", 23893f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 23993f2aa4dSMichal Nazarewicz }, 24093f2aa4dSMichal Nazarewicz }; 24193f2aa4dSMichal Nazarewicz 24293f2aa4dSMichal Nazarewicz 24393f2aa4dSMichal Nazarewicz static void init_thread(struct thread *t) 24493f2aa4dSMichal Nazarewicz { 24593f2aa4dSMichal Nazarewicz t->buf = malloc(t->buf_size); 24693f2aa4dSMichal Nazarewicz die_on(!t->buf, "malloc"); 24793f2aa4dSMichal Nazarewicz 24893f2aa4dSMichal Nazarewicz t->fd = open(t->filename, O_RDWR); 24993f2aa4dSMichal Nazarewicz die_on(t->fd < 0, "%s", t->filename); 25093f2aa4dSMichal Nazarewicz } 25193f2aa4dSMichal Nazarewicz 25293f2aa4dSMichal Nazarewicz static void cleanup_thread(void *arg) 25393f2aa4dSMichal Nazarewicz { 25493f2aa4dSMichal Nazarewicz struct thread *t = arg; 25593f2aa4dSMichal Nazarewicz int ret, fd; 25693f2aa4dSMichal Nazarewicz 25793f2aa4dSMichal Nazarewicz fd = t->fd; 25893f2aa4dSMichal Nazarewicz if (t->fd < 0) 25993f2aa4dSMichal Nazarewicz return; 26093f2aa4dSMichal Nazarewicz t->fd = -1; 26193f2aa4dSMichal Nazarewicz 26293f2aa4dSMichal Nazarewicz /* test the FIFO ioctls (non-ep0 code paths) */ 26393f2aa4dSMichal Nazarewicz if (t != threads) { 26493f2aa4dSMichal Nazarewicz ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS); 26593f2aa4dSMichal Nazarewicz if (ret < 0) { 26693f2aa4dSMichal Nazarewicz /* ENODEV reported after disconnect */ 26793f2aa4dSMichal Nazarewicz if (errno != ENODEV) 26893f2aa4dSMichal Nazarewicz err("%s: get fifo status", t->filename); 26993f2aa4dSMichal Nazarewicz } else if (ret) { 27093f2aa4dSMichal Nazarewicz warn("%s: unclaimed = %d\n", t->filename, ret); 27193f2aa4dSMichal Nazarewicz if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0) 27293f2aa4dSMichal Nazarewicz err("%s: fifo flush", t->filename); 27393f2aa4dSMichal Nazarewicz } 27493f2aa4dSMichal Nazarewicz } 27593f2aa4dSMichal Nazarewicz 27693f2aa4dSMichal Nazarewicz if (close(fd) < 0) 27793f2aa4dSMichal Nazarewicz err("%s: close", t->filename); 27893f2aa4dSMichal Nazarewicz 27993f2aa4dSMichal Nazarewicz free(t->buf); 28093f2aa4dSMichal Nazarewicz t->buf = NULL; 28193f2aa4dSMichal Nazarewicz } 28293f2aa4dSMichal Nazarewicz 28393f2aa4dSMichal Nazarewicz static void *start_thread_helper(void *arg) 28493f2aa4dSMichal Nazarewicz { 28593f2aa4dSMichal Nazarewicz const char *name, *op, *in_name, *out_name; 28693f2aa4dSMichal Nazarewicz struct thread *t = arg; 28793f2aa4dSMichal Nazarewicz ssize_t ret; 28893f2aa4dSMichal Nazarewicz 28993f2aa4dSMichal Nazarewicz info("%s: starts\n", t->filename); 29093f2aa4dSMichal Nazarewicz in_name = t->in_name ? t->in_name : t->filename; 29193f2aa4dSMichal Nazarewicz out_name = t->out_name ? t->out_name : t->filename; 29293f2aa4dSMichal Nazarewicz 29393f2aa4dSMichal Nazarewicz pthread_cleanup_push(cleanup_thread, arg); 29493f2aa4dSMichal Nazarewicz 29593f2aa4dSMichal Nazarewicz for (;;) { 29693f2aa4dSMichal Nazarewicz pthread_testcancel(); 29793f2aa4dSMichal Nazarewicz 29893f2aa4dSMichal Nazarewicz ret = t->in(t, t->buf, t->buf_size); 29993f2aa4dSMichal Nazarewicz if (ret > 0) { 30093f2aa4dSMichal Nazarewicz ret = t->out(t, t->buf, t->buf_size); 30193f2aa4dSMichal Nazarewicz name = out_name; 30293f2aa4dSMichal Nazarewicz op = "write"; 30393f2aa4dSMichal Nazarewicz } else { 30493f2aa4dSMichal Nazarewicz name = in_name; 30593f2aa4dSMichal Nazarewicz op = "read"; 30693f2aa4dSMichal Nazarewicz } 30793f2aa4dSMichal Nazarewicz 30893f2aa4dSMichal Nazarewicz if (ret > 0) { 30993f2aa4dSMichal Nazarewicz /* nop */ 31093f2aa4dSMichal Nazarewicz } else if (!ret) { 31193f2aa4dSMichal Nazarewicz debug("%s: %s: EOF", name, op); 31293f2aa4dSMichal Nazarewicz break; 31393f2aa4dSMichal Nazarewicz } else if (errno == EINTR || errno == EAGAIN) { 31493f2aa4dSMichal Nazarewicz debug("%s: %s", name, op); 31593f2aa4dSMichal Nazarewicz } else { 31693f2aa4dSMichal Nazarewicz warn("%s: %s", name, op); 31793f2aa4dSMichal Nazarewicz break; 31893f2aa4dSMichal Nazarewicz } 31993f2aa4dSMichal Nazarewicz } 32093f2aa4dSMichal Nazarewicz 32193f2aa4dSMichal Nazarewicz pthread_cleanup_pop(1); 32293f2aa4dSMichal Nazarewicz 32393f2aa4dSMichal Nazarewicz t->status = ret; 32493f2aa4dSMichal Nazarewicz info("%s: ends\n", t->filename); 32593f2aa4dSMichal Nazarewicz return NULL; 32693f2aa4dSMichal Nazarewicz } 32793f2aa4dSMichal Nazarewicz 32893f2aa4dSMichal Nazarewicz static void start_thread(struct thread *t) 32993f2aa4dSMichal Nazarewicz { 33093f2aa4dSMichal Nazarewicz debug("%s: starting\n", t->filename); 33193f2aa4dSMichal Nazarewicz 33293f2aa4dSMichal Nazarewicz die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0, 33393f2aa4dSMichal Nazarewicz "pthread_create(%s)", t->filename); 33493f2aa4dSMichal Nazarewicz } 33593f2aa4dSMichal Nazarewicz 33693f2aa4dSMichal Nazarewicz static void join_thread(struct thread *t) 33793f2aa4dSMichal Nazarewicz { 33893f2aa4dSMichal Nazarewicz int ret = pthread_join(t->id, NULL); 33993f2aa4dSMichal Nazarewicz 34093f2aa4dSMichal Nazarewicz if (ret < 0) 34193f2aa4dSMichal Nazarewicz err("%s: joining thread", t->filename); 34293f2aa4dSMichal Nazarewicz else 34393f2aa4dSMichal Nazarewicz debug("%s: joined\n", t->filename); 34493f2aa4dSMichal Nazarewicz } 34593f2aa4dSMichal Nazarewicz 34693f2aa4dSMichal Nazarewicz 34793f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes) 34893f2aa4dSMichal Nazarewicz { 34993f2aa4dSMichal Nazarewicz return read(t->fd, buf, nbytes); 35093f2aa4dSMichal Nazarewicz } 35193f2aa4dSMichal Nazarewicz 35293f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes) 35393f2aa4dSMichal Nazarewicz { 35493f2aa4dSMichal Nazarewicz return write(t->fd, buf, nbytes); 35593f2aa4dSMichal Nazarewicz } 35693f2aa4dSMichal Nazarewicz 35793f2aa4dSMichal Nazarewicz 35893f2aa4dSMichal Nazarewicz /******************** Empty/Fill buffer routines ****************************/ 35993f2aa4dSMichal Nazarewicz 36093f2aa4dSMichal Nazarewicz /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */ 36193f2aa4dSMichal Nazarewicz enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE }; 36293f2aa4dSMichal Nazarewicz static enum pattern pattern; 36393f2aa4dSMichal Nazarewicz 36493f2aa4dSMichal Nazarewicz static ssize_t 36593f2aa4dSMichal Nazarewicz fill_in_buf(struct thread *ignore, void *buf, size_t nbytes) 36693f2aa4dSMichal Nazarewicz { 36793f2aa4dSMichal Nazarewicz size_t i; 36893f2aa4dSMichal Nazarewicz __u8 *p; 36993f2aa4dSMichal Nazarewicz 37093f2aa4dSMichal Nazarewicz (void)ignore; 37193f2aa4dSMichal Nazarewicz 37293f2aa4dSMichal Nazarewicz switch (pattern) { 37393f2aa4dSMichal Nazarewicz case PAT_ZERO: 37493f2aa4dSMichal Nazarewicz memset(buf, 0, nbytes); 37593f2aa4dSMichal Nazarewicz break; 37693f2aa4dSMichal Nazarewicz 37793f2aa4dSMichal Nazarewicz case PAT_SEQ: 37893f2aa4dSMichal Nazarewicz for (p = buf, i = 0; i < nbytes; ++i, ++p) 37993f2aa4dSMichal Nazarewicz *p = i % 63; 38093f2aa4dSMichal Nazarewicz break; 38193f2aa4dSMichal Nazarewicz 38293f2aa4dSMichal Nazarewicz case PAT_PIPE: 38393f2aa4dSMichal Nazarewicz return fread(buf, 1, nbytes, stdin); 38493f2aa4dSMichal Nazarewicz } 38593f2aa4dSMichal Nazarewicz 38693f2aa4dSMichal Nazarewicz return nbytes; 38793f2aa4dSMichal Nazarewicz } 38893f2aa4dSMichal Nazarewicz 38993f2aa4dSMichal Nazarewicz static ssize_t 39093f2aa4dSMichal Nazarewicz empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes) 39193f2aa4dSMichal Nazarewicz { 39293f2aa4dSMichal Nazarewicz const __u8 *p; 39393f2aa4dSMichal Nazarewicz __u8 expected; 39493f2aa4dSMichal Nazarewicz ssize_t ret; 39593f2aa4dSMichal Nazarewicz size_t len; 39693f2aa4dSMichal Nazarewicz 39793f2aa4dSMichal Nazarewicz (void)ignore; 39893f2aa4dSMichal Nazarewicz 39993f2aa4dSMichal Nazarewicz switch (pattern) { 40093f2aa4dSMichal Nazarewicz case PAT_ZERO: 40193f2aa4dSMichal Nazarewicz expected = 0; 40293f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 40393f2aa4dSMichal Nazarewicz if (*p) 40493f2aa4dSMichal Nazarewicz goto invalid; 40593f2aa4dSMichal Nazarewicz break; 40693f2aa4dSMichal Nazarewicz 40793f2aa4dSMichal Nazarewicz case PAT_SEQ: 40893f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 40993f2aa4dSMichal Nazarewicz if (*p != len % 63) { 41093f2aa4dSMichal Nazarewicz expected = len % 63; 41193f2aa4dSMichal Nazarewicz goto invalid; 41293f2aa4dSMichal Nazarewicz } 41393f2aa4dSMichal Nazarewicz break; 41493f2aa4dSMichal Nazarewicz 41593f2aa4dSMichal Nazarewicz case PAT_PIPE: 41693f2aa4dSMichal Nazarewicz ret = fwrite(buf, nbytes, 1, stdout); 41793f2aa4dSMichal Nazarewicz if (ret > 0) 41893f2aa4dSMichal Nazarewicz fflush(stdout); 41993f2aa4dSMichal Nazarewicz break; 42093f2aa4dSMichal Nazarewicz 42193f2aa4dSMichal Nazarewicz invalid: 42293f2aa4dSMichal Nazarewicz err("bad OUT byte %zd, expected %02x got %02x\n", 42393f2aa4dSMichal Nazarewicz len, expected, *p); 42493f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) { 42593f2aa4dSMichal Nazarewicz if (0 == (len % 32)) 426d105e74eSDavidlohr Bueso fprintf(stderr, "%4zd:", len); 42793f2aa4dSMichal Nazarewicz fprintf(stderr, " %02x", *p); 42893f2aa4dSMichal Nazarewicz if (31 == (len % 32)) 42993f2aa4dSMichal Nazarewicz fprintf(stderr, "\n"); 43093f2aa4dSMichal Nazarewicz } 43193f2aa4dSMichal Nazarewicz fflush(stderr); 43293f2aa4dSMichal Nazarewicz errno = EILSEQ; 43393f2aa4dSMichal Nazarewicz return -1; 43493f2aa4dSMichal Nazarewicz } 43593f2aa4dSMichal Nazarewicz 43693f2aa4dSMichal Nazarewicz return len; 43793f2aa4dSMichal Nazarewicz } 43893f2aa4dSMichal Nazarewicz 43993f2aa4dSMichal Nazarewicz 44093f2aa4dSMichal Nazarewicz /******************** Endpoints routines ************************************/ 44193f2aa4dSMichal Nazarewicz 44293f2aa4dSMichal Nazarewicz static void handle_setup(const struct usb_ctrlrequest *setup) 44393f2aa4dSMichal Nazarewicz { 44493f2aa4dSMichal Nazarewicz printf("bRequestType = %d\n", setup->bRequestType); 44593f2aa4dSMichal Nazarewicz printf("bRequest = %d\n", setup->bRequest); 44693f2aa4dSMichal Nazarewicz printf("wValue = %d\n", le16_to_cpu(setup->wValue)); 44793f2aa4dSMichal Nazarewicz printf("wIndex = %d\n", le16_to_cpu(setup->wIndex)); 44893f2aa4dSMichal Nazarewicz printf("wLength = %d\n", le16_to_cpu(setup->wLength)); 44993f2aa4dSMichal Nazarewicz } 45093f2aa4dSMichal Nazarewicz 45193f2aa4dSMichal Nazarewicz static ssize_t 45293f2aa4dSMichal Nazarewicz ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) 45393f2aa4dSMichal Nazarewicz { 45493f2aa4dSMichal Nazarewicz static const char *const names[] = { 45593f2aa4dSMichal Nazarewicz [FUNCTIONFS_BIND] = "BIND", 45693f2aa4dSMichal Nazarewicz [FUNCTIONFS_UNBIND] = "UNBIND", 45793f2aa4dSMichal Nazarewicz [FUNCTIONFS_ENABLE] = "ENABLE", 45893f2aa4dSMichal Nazarewicz [FUNCTIONFS_DISABLE] = "DISABLE", 45993f2aa4dSMichal Nazarewicz [FUNCTIONFS_SETUP] = "SETUP", 46093f2aa4dSMichal Nazarewicz [FUNCTIONFS_SUSPEND] = "SUSPEND", 46193f2aa4dSMichal Nazarewicz [FUNCTIONFS_RESUME] = "RESUME", 46293f2aa4dSMichal Nazarewicz }; 46393f2aa4dSMichal Nazarewicz 46493f2aa4dSMichal Nazarewicz const struct usb_functionfs_event *event = buf; 46593f2aa4dSMichal Nazarewicz size_t n; 46693f2aa4dSMichal Nazarewicz 46793f2aa4dSMichal Nazarewicz (void)ignore; 46893f2aa4dSMichal Nazarewicz 46993f2aa4dSMichal Nazarewicz for (n = nbytes / sizeof *event; n; --n, ++event) 47093f2aa4dSMichal Nazarewicz switch (event->type) { 47193f2aa4dSMichal Nazarewicz case FUNCTIONFS_BIND: 47293f2aa4dSMichal Nazarewicz case FUNCTIONFS_UNBIND: 47393f2aa4dSMichal Nazarewicz case FUNCTIONFS_ENABLE: 47493f2aa4dSMichal Nazarewicz case FUNCTIONFS_DISABLE: 47593f2aa4dSMichal Nazarewicz case FUNCTIONFS_SETUP: 47693f2aa4dSMichal Nazarewicz case FUNCTIONFS_SUSPEND: 47793f2aa4dSMichal Nazarewicz case FUNCTIONFS_RESUME: 47893f2aa4dSMichal Nazarewicz printf("Event %s\n", names[event->type]); 47993f2aa4dSMichal Nazarewicz if (event->type == FUNCTIONFS_SETUP) 48093f2aa4dSMichal Nazarewicz handle_setup(&event->u.setup); 48193f2aa4dSMichal Nazarewicz break; 48293f2aa4dSMichal Nazarewicz 48393f2aa4dSMichal Nazarewicz default: 48493f2aa4dSMichal Nazarewicz printf("Event %03u (unknown)\n", event->type); 48593f2aa4dSMichal Nazarewicz } 48693f2aa4dSMichal Nazarewicz 48793f2aa4dSMichal Nazarewicz return nbytes; 48893f2aa4dSMichal Nazarewicz } 48993f2aa4dSMichal Nazarewicz 49093f2aa4dSMichal Nazarewicz static void ep0_init(struct thread *t) 49193f2aa4dSMichal Nazarewicz { 49293f2aa4dSMichal Nazarewicz ssize_t ret; 49393f2aa4dSMichal Nazarewicz 49493f2aa4dSMichal Nazarewicz info("%s: writing descriptors\n", t->filename); 49593f2aa4dSMichal Nazarewicz ret = write(t->fd, &descriptors, sizeof descriptors); 49693f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: descriptors", t->filename); 49793f2aa4dSMichal Nazarewicz 49893f2aa4dSMichal Nazarewicz info("%s: writing strings\n", t->filename); 49993f2aa4dSMichal Nazarewicz ret = write(t->fd, &strings, sizeof strings); 50093f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: strings", t->filename); 50193f2aa4dSMichal Nazarewicz } 50293f2aa4dSMichal Nazarewicz 50393f2aa4dSMichal Nazarewicz 50493f2aa4dSMichal Nazarewicz /******************** Main **************************************************/ 50593f2aa4dSMichal Nazarewicz 50693f2aa4dSMichal Nazarewicz int main(void) 50793f2aa4dSMichal Nazarewicz { 50893f2aa4dSMichal Nazarewicz unsigned i; 50993f2aa4dSMichal Nazarewicz 51093f2aa4dSMichal Nazarewicz /* XXX TODO: Argument parsing missing */ 51193f2aa4dSMichal Nazarewicz 51293f2aa4dSMichal Nazarewicz init_thread(threads); 51393f2aa4dSMichal Nazarewicz ep0_init(threads); 51493f2aa4dSMichal Nazarewicz 51593f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 51693f2aa4dSMichal Nazarewicz init_thread(threads + i); 51793f2aa4dSMichal Nazarewicz 51893f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 51993f2aa4dSMichal Nazarewicz start_thread(threads + i); 52093f2aa4dSMichal Nazarewicz 52193f2aa4dSMichal Nazarewicz start_thread_helper(threads); 52293f2aa4dSMichal Nazarewicz 52393f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 52493f2aa4dSMichal Nazarewicz join_thread(threads + i); 52593f2aa4dSMichal Nazarewicz 52693f2aa4dSMichal Nazarewicz return 0; 52793f2aa4dSMichal Nazarewicz } 528