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> 3993f2aa4dSMichal Nazarewicz 40*d105e74eSDavidlohr Bueso #include "../../include/linux/usb/functionfs.h" 4193f2aa4dSMichal Nazarewicz 4293f2aa4dSMichal Nazarewicz 4393f2aa4dSMichal Nazarewicz /******************** Little Endian Handling ********************************/ 4493f2aa4dSMichal Nazarewicz 4593f2aa4dSMichal Nazarewicz #define cpu_to_le16(x) htole16(x) 4693f2aa4dSMichal Nazarewicz #define cpu_to_le32(x) htole32(x) 4793f2aa4dSMichal Nazarewicz #define le32_to_cpu(x) le32toh(x) 4893f2aa4dSMichal Nazarewicz #define le16_to_cpu(x) le16toh(x) 4993f2aa4dSMichal Nazarewicz 5093f2aa4dSMichal Nazarewicz static inline __u16 get_unaligned_le16(const void *_ptr) 5193f2aa4dSMichal Nazarewicz { 5293f2aa4dSMichal Nazarewicz const __u8 *ptr = _ptr; 5393f2aa4dSMichal Nazarewicz return ptr[0] | (ptr[1] << 8); 5493f2aa4dSMichal Nazarewicz } 5593f2aa4dSMichal Nazarewicz 5693f2aa4dSMichal Nazarewicz static inline __u32 get_unaligned_le32(const void *_ptr) 5793f2aa4dSMichal Nazarewicz { 5893f2aa4dSMichal Nazarewicz const __u8 *ptr = _ptr; 5993f2aa4dSMichal Nazarewicz return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); 6093f2aa4dSMichal Nazarewicz } 6193f2aa4dSMichal Nazarewicz 6293f2aa4dSMichal Nazarewicz static inline void put_unaligned_le16(__u16 val, void *_ptr) 6393f2aa4dSMichal Nazarewicz { 6493f2aa4dSMichal Nazarewicz __u8 *ptr = _ptr; 6593f2aa4dSMichal Nazarewicz *ptr++ = val; 6693f2aa4dSMichal Nazarewicz *ptr++ = val >> 8; 6793f2aa4dSMichal Nazarewicz } 6893f2aa4dSMichal Nazarewicz 6993f2aa4dSMichal Nazarewicz static inline void put_unaligned_le32(__u32 val, void *_ptr) 7093f2aa4dSMichal Nazarewicz { 7193f2aa4dSMichal Nazarewicz __u8 *ptr = _ptr; 7293f2aa4dSMichal Nazarewicz *ptr++ = val; 7393f2aa4dSMichal Nazarewicz *ptr++ = val >> 8; 7493f2aa4dSMichal Nazarewicz *ptr++ = val >> 16; 7593f2aa4dSMichal Nazarewicz *ptr++ = val >> 24; 7693f2aa4dSMichal Nazarewicz } 7793f2aa4dSMichal Nazarewicz 7893f2aa4dSMichal Nazarewicz 7993f2aa4dSMichal Nazarewicz /******************** Messages and Errors ***********************************/ 8093f2aa4dSMichal Nazarewicz 8193f2aa4dSMichal Nazarewicz static const char argv0[] = "ffs-test"; 8293f2aa4dSMichal Nazarewicz 8393f2aa4dSMichal Nazarewicz static unsigned verbosity = 7; 8493f2aa4dSMichal Nazarewicz 8593f2aa4dSMichal Nazarewicz static void _msg(unsigned level, const char *fmt, ...) 8693f2aa4dSMichal Nazarewicz { 8793f2aa4dSMichal Nazarewicz if (level < 2) 8893f2aa4dSMichal Nazarewicz level = 2; 8993f2aa4dSMichal Nazarewicz else if (level > 7) 9093f2aa4dSMichal Nazarewicz level = 7; 9193f2aa4dSMichal Nazarewicz 9293f2aa4dSMichal Nazarewicz if (level <= verbosity) { 9393f2aa4dSMichal Nazarewicz static const char levels[8][6] = { 9493f2aa4dSMichal Nazarewicz [2] = "crit:", 9593f2aa4dSMichal Nazarewicz [3] = "err: ", 9693f2aa4dSMichal Nazarewicz [4] = "warn:", 9793f2aa4dSMichal Nazarewicz [5] = "note:", 9893f2aa4dSMichal Nazarewicz [6] = "info:", 9993f2aa4dSMichal Nazarewicz [7] = "dbg: " 10093f2aa4dSMichal Nazarewicz }; 10193f2aa4dSMichal Nazarewicz 10293f2aa4dSMichal Nazarewicz int _errno = errno; 10393f2aa4dSMichal Nazarewicz va_list ap; 10493f2aa4dSMichal Nazarewicz 10593f2aa4dSMichal Nazarewicz fprintf(stderr, "%s: %s ", argv0, levels[level]); 10693f2aa4dSMichal Nazarewicz va_start(ap, fmt); 10793f2aa4dSMichal Nazarewicz vfprintf(stderr, fmt, ap); 10893f2aa4dSMichal Nazarewicz va_end(ap); 10993f2aa4dSMichal Nazarewicz 11093f2aa4dSMichal Nazarewicz if (fmt[strlen(fmt) - 1] != '\n') { 11193f2aa4dSMichal Nazarewicz char buffer[128]; 11293f2aa4dSMichal Nazarewicz strerror_r(_errno, buffer, sizeof buffer); 11393f2aa4dSMichal Nazarewicz fprintf(stderr, ": (-%d) %s\n", _errno, buffer); 11493f2aa4dSMichal Nazarewicz } 11593f2aa4dSMichal Nazarewicz 11693f2aa4dSMichal Nazarewicz fflush(stderr); 11793f2aa4dSMichal Nazarewicz } 11893f2aa4dSMichal Nazarewicz } 11993f2aa4dSMichal Nazarewicz 12093f2aa4dSMichal Nazarewicz #define die(...) (_msg(2, __VA_ARGS__), exit(1)) 12193f2aa4dSMichal Nazarewicz #define err(...) _msg(3, __VA_ARGS__) 12293f2aa4dSMichal Nazarewicz #define warn(...) _msg(4, __VA_ARGS__) 12393f2aa4dSMichal Nazarewicz #define note(...) _msg(5, __VA_ARGS__) 12493f2aa4dSMichal Nazarewicz #define info(...) _msg(6, __VA_ARGS__) 12593f2aa4dSMichal Nazarewicz #define debug(...) _msg(7, __VA_ARGS__) 12693f2aa4dSMichal Nazarewicz 12793f2aa4dSMichal Nazarewicz #define die_on(cond, ...) do { \ 12893f2aa4dSMichal Nazarewicz if (cond) \ 12993f2aa4dSMichal Nazarewicz die(__VA_ARGS__); \ 13093f2aa4dSMichal Nazarewicz } while (0) 13193f2aa4dSMichal Nazarewicz 13293f2aa4dSMichal Nazarewicz 13393f2aa4dSMichal Nazarewicz /******************** Descriptors and Strings *******************************/ 13493f2aa4dSMichal Nazarewicz 13593f2aa4dSMichal Nazarewicz static const struct { 13693f2aa4dSMichal Nazarewicz struct usb_functionfs_descs_head header; 13793f2aa4dSMichal Nazarewicz struct { 13893f2aa4dSMichal Nazarewicz struct usb_interface_descriptor intf; 13993f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio sink; 14093f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio source; 14193f2aa4dSMichal Nazarewicz } __attribute__((packed)) fs_descs, hs_descs; 14293f2aa4dSMichal Nazarewicz } __attribute__((packed)) descriptors = { 14393f2aa4dSMichal Nazarewicz .header = { 14493f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), 14593f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof descriptors), 14693f2aa4dSMichal Nazarewicz .fs_count = 3, 14793f2aa4dSMichal Nazarewicz .hs_count = 3, 14893f2aa4dSMichal Nazarewicz }, 14993f2aa4dSMichal Nazarewicz .fs_descs = { 15093f2aa4dSMichal Nazarewicz .intf = { 15193f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 15293f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 15393f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 15493f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 15593f2aa4dSMichal Nazarewicz .iInterface = 1, 15693f2aa4dSMichal Nazarewicz }, 15793f2aa4dSMichal Nazarewicz .sink = { 15893f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.sink, 15993f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 16093f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 16193f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 16293f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 16393f2aa4dSMichal Nazarewicz }, 16493f2aa4dSMichal Nazarewicz .source = { 16593f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.source, 16693f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 16793f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 16893f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 16993f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 17093f2aa4dSMichal Nazarewicz }, 17193f2aa4dSMichal Nazarewicz }, 17293f2aa4dSMichal Nazarewicz .hs_descs = { 17393f2aa4dSMichal Nazarewicz .intf = { 17493f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 17593f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 17693f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 17793f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 17893f2aa4dSMichal Nazarewicz .iInterface = 1, 17993f2aa4dSMichal Nazarewicz }, 18093f2aa4dSMichal Nazarewicz .sink = { 18193f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.sink, 18293f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 18393f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 18493f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 18593f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512), 18693f2aa4dSMichal Nazarewicz }, 18793f2aa4dSMichal Nazarewicz .source = { 18893f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.source, 18993f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 19093f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 19193f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 19293f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512), 19393f2aa4dSMichal Nazarewicz .bInterval = 1, /* NAK every 1 uframe */ 19493f2aa4dSMichal Nazarewicz }, 19593f2aa4dSMichal Nazarewicz }, 19693f2aa4dSMichal Nazarewicz }; 19793f2aa4dSMichal Nazarewicz 19893f2aa4dSMichal Nazarewicz 19993f2aa4dSMichal Nazarewicz #define STR_INTERFACE_ "Source/Sink" 20093f2aa4dSMichal Nazarewicz 20193f2aa4dSMichal Nazarewicz static const struct { 20293f2aa4dSMichal Nazarewicz struct usb_functionfs_strings_head header; 20393f2aa4dSMichal Nazarewicz struct { 20493f2aa4dSMichal Nazarewicz __le16 code; 20593f2aa4dSMichal Nazarewicz const char str1[sizeof STR_INTERFACE_]; 20693f2aa4dSMichal Nazarewicz } __attribute__((packed)) lang0; 20793f2aa4dSMichal Nazarewicz } __attribute__((packed)) strings = { 20893f2aa4dSMichal Nazarewicz .header = { 20993f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), 21093f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof strings), 21193f2aa4dSMichal Nazarewicz .str_count = cpu_to_le32(1), 21293f2aa4dSMichal Nazarewicz .lang_count = cpu_to_le32(1), 21393f2aa4dSMichal Nazarewicz }, 21493f2aa4dSMichal Nazarewicz .lang0 = { 21593f2aa4dSMichal Nazarewicz cpu_to_le16(0x0409), /* en-us */ 21693f2aa4dSMichal Nazarewicz STR_INTERFACE_, 21793f2aa4dSMichal Nazarewicz }, 21893f2aa4dSMichal Nazarewicz }; 21993f2aa4dSMichal Nazarewicz 22093f2aa4dSMichal Nazarewicz #define STR_INTERFACE strings.lang0.str1 22193f2aa4dSMichal Nazarewicz 22293f2aa4dSMichal Nazarewicz 22393f2aa4dSMichal Nazarewicz /******************** Files and Threads Handling ****************************/ 22493f2aa4dSMichal Nazarewicz 22593f2aa4dSMichal Nazarewicz struct thread; 22693f2aa4dSMichal Nazarewicz 22793f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes); 22893f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes); 22993f2aa4dSMichal Nazarewicz static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes); 23093f2aa4dSMichal Nazarewicz static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes); 23193f2aa4dSMichal Nazarewicz static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes); 23293f2aa4dSMichal Nazarewicz 23393f2aa4dSMichal Nazarewicz 23493f2aa4dSMichal Nazarewicz static struct thread { 23593f2aa4dSMichal Nazarewicz const char *const filename; 23693f2aa4dSMichal Nazarewicz size_t buf_size; 23793f2aa4dSMichal Nazarewicz 23893f2aa4dSMichal Nazarewicz ssize_t (*in)(struct thread *, void *, size_t); 23993f2aa4dSMichal Nazarewicz const char *const in_name; 24093f2aa4dSMichal Nazarewicz 24193f2aa4dSMichal Nazarewicz ssize_t (*out)(struct thread *, const void *, size_t); 24293f2aa4dSMichal Nazarewicz const char *const out_name; 24393f2aa4dSMichal Nazarewicz 24493f2aa4dSMichal Nazarewicz int fd; 24593f2aa4dSMichal Nazarewicz pthread_t id; 24693f2aa4dSMichal Nazarewicz void *buf; 24793f2aa4dSMichal Nazarewicz ssize_t status; 24893f2aa4dSMichal Nazarewicz } threads[] = { 24993f2aa4dSMichal Nazarewicz { 25093f2aa4dSMichal Nazarewicz "ep0", 4 * sizeof(struct usb_functionfs_event), 25193f2aa4dSMichal Nazarewicz read_wrap, NULL, 25293f2aa4dSMichal Nazarewicz ep0_consume, "<consume>", 25393f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 25493f2aa4dSMichal Nazarewicz }, 25593f2aa4dSMichal Nazarewicz { 25693f2aa4dSMichal Nazarewicz "ep1", 8 * 1024, 25793f2aa4dSMichal Nazarewicz fill_in_buf, "<in>", 25893f2aa4dSMichal Nazarewicz write_wrap, NULL, 25993f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 26093f2aa4dSMichal Nazarewicz }, 26193f2aa4dSMichal Nazarewicz { 26293f2aa4dSMichal Nazarewicz "ep2", 8 * 1024, 26393f2aa4dSMichal Nazarewicz read_wrap, NULL, 26493f2aa4dSMichal Nazarewicz empty_out_buf, "<out>", 26593f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 26693f2aa4dSMichal Nazarewicz }, 26793f2aa4dSMichal Nazarewicz }; 26893f2aa4dSMichal Nazarewicz 26993f2aa4dSMichal Nazarewicz 27093f2aa4dSMichal Nazarewicz static void init_thread(struct thread *t) 27193f2aa4dSMichal Nazarewicz { 27293f2aa4dSMichal Nazarewicz t->buf = malloc(t->buf_size); 27393f2aa4dSMichal Nazarewicz die_on(!t->buf, "malloc"); 27493f2aa4dSMichal Nazarewicz 27593f2aa4dSMichal Nazarewicz t->fd = open(t->filename, O_RDWR); 27693f2aa4dSMichal Nazarewicz die_on(t->fd < 0, "%s", t->filename); 27793f2aa4dSMichal Nazarewicz } 27893f2aa4dSMichal Nazarewicz 27993f2aa4dSMichal Nazarewicz static void cleanup_thread(void *arg) 28093f2aa4dSMichal Nazarewicz { 28193f2aa4dSMichal Nazarewicz struct thread *t = arg; 28293f2aa4dSMichal Nazarewicz int ret, fd; 28393f2aa4dSMichal Nazarewicz 28493f2aa4dSMichal Nazarewicz fd = t->fd; 28593f2aa4dSMichal Nazarewicz if (t->fd < 0) 28693f2aa4dSMichal Nazarewicz return; 28793f2aa4dSMichal Nazarewicz t->fd = -1; 28893f2aa4dSMichal Nazarewicz 28993f2aa4dSMichal Nazarewicz /* test the FIFO ioctls (non-ep0 code paths) */ 29093f2aa4dSMichal Nazarewicz if (t != threads) { 29193f2aa4dSMichal Nazarewicz ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS); 29293f2aa4dSMichal Nazarewicz if (ret < 0) { 29393f2aa4dSMichal Nazarewicz /* ENODEV reported after disconnect */ 29493f2aa4dSMichal Nazarewicz if (errno != ENODEV) 29593f2aa4dSMichal Nazarewicz err("%s: get fifo status", t->filename); 29693f2aa4dSMichal Nazarewicz } else if (ret) { 29793f2aa4dSMichal Nazarewicz warn("%s: unclaimed = %d\n", t->filename, ret); 29893f2aa4dSMichal Nazarewicz if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0) 29993f2aa4dSMichal Nazarewicz err("%s: fifo flush", t->filename); 30093f2aa4dSMichal Nazarewicz } 30193f2aa4dSMichal Nazarewicz } 30293f2aa4dSMichal Nazarewicz 30393f2aa4dSMichal Nazarewicz if (close(fd) < 0) 30493f2aa4dSMichal Nazarewicz err("%s: close", t->filename); 30593f2aa4dSMichal Nazarewicz 30693f2aa4dSMichal Nazarewicz free(t->buf); 30793f2aa4dSMichal Nazarewicz t->buf = NULL; 30893f2aa4dSMichal Nazarewicz } 30993f2aa4dSMichal Nazarewicz 31093f2aa4dSMichal Nazarewicz static void *start_thread_helper(void *arg) 31193f2aa4dSMichal Nazarewicz { 31293f2aa4dSMichal Nazarewicz const char *name, *op, *in_name, *out_name; 31393f2aa4dSMichal Nazarewicz struct thread *t = arg; 31493f2aa4dSMichal Nazarewicz ssize_t ret; 31593f2aa4dSMichal Nazarewicz 31693f2aa4dSMichal Nazarewicz info("%s: starts\n", t->filename); 31793f2aa4dSMichal Nazarewicz in_name = t->in_name ? t->in_name : t->filename; 31893f2aa4dSMichal Nazarewicz out_name = t->out_name ? t->out_name : t->filename; 31993f2aa4dSMichal Nazarewicz 32093f2aa4dSMichal Nazarewicz pthread_cleanup_push(cleanup_thread, arg); 32193f2aa4dSMichal Nazarewicz 32293f2aa4dSMichal Nazarewicz for (;;) { 32393f2aa4dSMichal Nazarewicz pthread_testcancel(); 32493f2aa4dSMichal Nazarewicz 32593f2aa4dSMichal Nazarewicz ret = t->in(t, t->buf, t->buf_size); 32693f2aa4dSMichal Nazarewicz if (ret > 0) { 32793f2aa4dSMichal Nazarewicz ret = t->out(t, t->buf, t->buf_size); 32893f2aa4dSMichal Nazarewicz name = out_name; 32993f2aa4dSMichal Nazarewicz op = "write"; 33093f2aa4dSMichal Nazarewicz } else { 33193f2aa4dSMichal Nazarewicz name = in_name; 33293f2aa4dSMichal Nazarewicz op = "read"; 33393f2aa4dSMichal Nazarewicz } 33493f2aa4dSMichal Nazarewicz 33593f2aa4dSMichal Nazarewicz if (ret > 0) { 33693f2aa4dSMichal Nazarewicz /* nop */ 33793f2aa4dSMichal Nazarewicz } else if (!ret) { 33893f2aa4dSMichal Nazarewicz debug("%s: %s: EOF", name, op); 33993f2aa4dSMichal Nazarewicz break; 34093f2aa4dSMichal Nazarewicz } else if (errno == EINTR || errno == EAGAIN) { 34193f2aa4dSMichal Nazarewicz debug("%s: %s", name, op); 34293f2aa4dSMichal Nazarewicz } else { 34393f2aa4dSMichal Nazarewicz warn("%s: %s", name, op); 34493f2aa4dSMichal Nazarewicz break; 34593f2aa4dSMichal Nazarewicz } 34693f2aa4dSMichal Nazarewicz } 34793f2aa4dSMichal Nazarewicz 34893f2aa4dSMichal Nazarewicz pthread_cleanup_pop(1); 34993f2aa4dSMichal Nazarewicz 35093f2aa4dSMichal Nazarewicz t->status = ret; 35193f2aa4dSMichal Nazarewicz info("%s: ends\n", t->filename); 35293f2aa4dSMichal Nazarewicz return NULL; 35393f2aa4dSMichal Nazarewicz } 35493f2aa4dSMichal Nazarewicz 35593f2aa4dSMichal Nazarewicz static void start_thread(struct thread *t) 35693f2aa4dSMichal Nazarewicz { 35793f2aa4dSMichal Nazarewicz debug("%s: starting\n", t->filename); 35893f2aa4dSMichal Nazarewicz 35993f2aa4dSMichal Nazarewicz die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0, 36093f2aa4dSMichal Nazarewicz "pthread_create(%s)", t->filename); 36193f2aa4dSMichal Nazarewicz } 36293f2aa4dSMichal Nazarewicz 36393f2aa4dSMichal Nazarewicz static void join_thread(struct thread *t) 36493f2aa4dSMichal Nazarewicz { 36593f2aa4dSMichal Nazarewicz int ret = pthread_join(t->id, NULL); 36693f2aa4dSMichal Nazarewicz 36793f2aa4dSMichal Nazarewicz if (ret < 0) 36893f2aa4dSMichal Nazarewicz err("%s: joining thread", t->filename); 36993f2aa4dSMichal Nazarewicz else 37093f2aa4dSMichal Nazarewicz debug("%s: joined\n", t->filename); 37193f2aa4dSMichal Nazarewicz } 37293f2aa4dSMichal Nazarewicz 37393f2aa4dSMichal Nazarewicz 37493f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes) 37593f2aa4dSMichal Nazarewicz { 37693f2aa4dSMichal Nazarewicz return read(t->fd, buf, nbytes); 37793f2aa4dSMichal Nazarewicz } 37893f2aa4dSMichal Nazarewicz 37993f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes) 38093f2aa4dSMichal Nazarewicz { 38193f2aa4dSMichal Nazarewicz return write(t->fd, buf, nbytes); 38293f2aa4dSMichal Nazarewicz } 38393f2aa4dSMichal Nazarewicz 38493f2aa4dSMichal Nazarewicz 38593f2aa4dSMichal Nazarewicz /******************** Empty/Fill buffer routines ****************************/ 38693f2aa4dSMichal Nazarewicz 38793f2aa4dSMichal Nazarewicz /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */ 38893f2aa4dSMichal Nazarewicz enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE }; 38993f2aa4dSMichal Nazarewicz static enum pattern pattern; 39093f2aa4dSMichal Nazarewicz 39193f2aa4dSMichal Nazarewicz static ssize_t 39293f2aa4dSMichal Nazarewicz fill_in_buf(struct thread *ignore, void *buf, size_t nbytes) 39393f2aa4dSMichal Nazarewicz { 39493f2aa4dSMichal Nazarewicz size_t i; 39593f2aa4dSMichal Nazarewicz __u8 *p; 39693f2aa4dSMichal Nazarewicz 39793f2aa4dSMichal Nazarewicz (void)ignore; 39893f2aa4dSMichal Nazarewicz 39993f2aa4dSMichal Nazarewicz switch (pattern) { 40093f2aa4dSMichal Nazarewicz case PAT_ZERO: 40193f2aa4dSMichal Nazarewicz memset(buf, 0, nbytes); 40293f2aa4dSMichal Nazarewicz break; 40393f2aa4dSMichal Nazarewicz 40493f2aa4dSMichal Nazarewicz case PAT_SEQ: 40593f2aa4dSMichal Nazarewicz for (p = buf, i = 0; i < nbytes; ++i, ++p) 40693f2aa4dSMichal Nazarewicz *p = i % 63; 40793f2aa4dSMichal Nazarewicz break; 40893f2aa4dSMichal Nazarewicz 40993f2aa4dSMichal Nazarewicz case PAT_PIPE: 41093f2aa4dSMichal Nazarewicz return fread(buf, 1, nbytes, stdin); 41193f2aa4dSMichal Nazarewicz } 41293f2aa4dSMichal Nazarewicz 41393f2aa4dSMichal Nazarewicz return nbytes; 41493f2aa4dSMichal Nazarewicz } 41593f2aa4dSMichal Nazarewicz 41693f2aa4dSMichal Nazarewicz static ssize_t 41793f2aa4dSMichal Nazarewicz empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes) 41893f2aa4dSMichal Nazarewicz { 41993f2aa4dSMichal Nazarewicz const __u8 *p; 42093f2aa4dSMichal Nazarewicz __u8 expected; 42193f2aa4dSMichal Nazarewicz ssize_t ret; 42293f2aa4dSMichal Nazarewicz size_t len; 42393f2aa4dSMichal Nazarewicz 42493f2aa4dSMichal Nazarewicz (void)ignore; 42593f2aa4dSMichal Nazarewicz 42693f2aa4dSMichal Nazarewicz switch (pattern) { 42793f2aa4dSMichal Nazarewicz case PAT_ZERO: 42893f2aa4dSMichal Nazarewicz expected = 0; 42993f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 43093f2aa4dSMichal Nazarewicz if (*p) 43193f2aa4dSMichal Nazarewicz goto invalid; 43293f2aa4dSMichal Nazarewicz break; 43393f2aa4dSMichal Nazarewicz 43493f2aa4dSMichal Nazarewicz case PAT_SEQ: 43593f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 43693f2aa4dSMichal Nazarewicz if (*p != len % 63) { 43793f2aa4dSMichal Nazarewicz expected = len % 63; 43893f2aa4dSMichal Nazarewicz goto invalid; 43993f2aa4dSMichal Nazarewicz } 44093f2aa4dSMichal Nazarewicz break; 44193f2aa4dSMichal Nazarewicz 44293f2aa4dSMichal Nazarewicz case PAT_PIPE: 44393f2aa4dSMichal Nazarewicz ret = fwrite(buf, nbytes, 1, stdout); 44493f2aa4dSMichal Nazarewicz if (ret > 0) 44593f2aa4dSMichal Nazarewicz fflush(stdout); 44693f2aa4dSMichal Nazarewicz break; 44793f2aa4dSMichal Nazarewicz 44893f2aa4dSMichal Nazarewicz invalid: 44993f2aa4dSMichal Nazarewicz err("bad OUT byte %zd, expected %02x got %02x\n", 45093f2aa4dSMichal Nazarewicz len, expected, *p); 45193f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) { 45293f2aa4dSMichal Nazarewicz if (0 == (len % 32)) 453*d105e74eSDavidlohr Bueso fprintf(stderr, "%4zd:", len); 45493f2aa4dSMichal Nazarewicz fprintf(stderr, " %02x", *p); 45593f2aa4dSMichal Nazarewicz if (31 == (len % 32)) 45693f2aa4dSMichal Nazarewicz fprintf(stderr, "\n"); 45793f2aa4dSMichal Nazarewicz } 45893f2aa4dSMichal Nazarewicz fflush(stderr); 45993f2aa4dSMichal Nazarewicz errno = EILSEQ; 46093f2aa4dSMichal Nazarewicz return -1; 46193f2aa4dSMichal Nazarewicz } 46293f2aa4dSMichal Nazarewicz 46393f2aa4dSMichal Nazarewicz return len; 46493f2aa4dSMichal Nazarewicz } 46593f2aa4dSMichal Nazarewicz 46693f2aa4dSMichal Nazarewicz 46793f2aa4dSMichal Nazarewicz /******************** Endpoints routines ************************************/ 46893f2aa4dSMichal Nazarewicz 46993f2aa4dSMichal Nazarewicz static void handle_setup(const struct usb_ctrlrequest *setup) 47093f2aa4dSMichal Nazarewicz { 47193f2aa4dSMichal Nazarewicz printf("bRequestType = %d\n", setup->bRequestType); 47293f2aa4dSMichal Nazarewicz printf("bRequest = %d\n", setup->bRequest); 47393f2aa4dSMichal Nazarewicz printf("wValue = %d\n", le16_to_cpu(setup->wValue)); 47493f2aa4dSMichal Nazarewicz printf("wIndex = %d\n", le16_to_cpu(setup->wIndex)); 47593f2aa4dSMichal Nazarewicz printf("wLength = %d\n", le16_to_cpu(setup->wLength)); 47693f2aa4dSMichal Nazarewicz } 47793f2aa4dSMichal Nazarewicz 47893f2aa4dSMichal Nazarewicz static ssize_t 47993f2aa4dSMichal Nazarewicz ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) 48093f2aa4dSMichal Nazarewicz { 48193f2aa4dSMichal Nazarewicz static const char *const names[] = { 48293f2aa4dSMichal Nazarewicz [FUNCTIONFS_BIND] = "BIND", 48393f2aa4dSMichal Nazarewicz [FUNCTIONFS_UNBIND] = "UNBIND", 48493f2aa4dSMichal Nazarewicz [FUNCTIONFS_ENABLE] = "ENABLE", 48593f2aa4dSMichal Nazarewicz [FUNCTIONFS_DISABLE] = "DISABLE", 48693f2aa4dSMichal Nazarewicz [FUNCTIONFS_SETUP] = "SETUP", 48793f2aa4dSMichal Nazarewicz [FUNCTIONFS_SUSPEND] = "SUSPEND", 48893f2aa4dSMichal Nazarewicz [FUNCTIONFS_RESUME] = "RESUME", 48993f2aa4dSMichal Nazarewicz }; 49093f2aa4dSMichal Nazarewicz 49193f2aa4dSMichal Nazarewicz const struct usb_functionfs_event *event = buf; 49293f2aa4dSMichal Nazarewicz size_t n; 49393f2aa4dSMichal Nazarewicz 49493f2aa4dSMichal Nazarewicz (void)ignore; 49593f2aa4dSMichal Nazarewicz 49693f2aa4dSMichal Nazarewicz for (n = nbytes / sizeof *event; n; --n, ++event) 49793f2aa4dSMichal Nazarewicz switch (event->type) { 49893f2aa4dSMichal Nazarewicz case FUNCTIONFS_BIND: 49993f2aa4dSMichal Nazarewicz case FUNCTIONFS_UNBIND: 50093f2aa4dSMichal Nazarewicz case FUNCTIONFS_ENABLE: 50193f2aa4dSMichal Nazarewicz case FUNCTIONFS_DISABLE: 50293f2aa4dSMichal Nazarewicz case FUNCTIONFS_SETUP: 50393f2aa4dSMichal Nazarewicz case FUNCTIONFS_SUSPEND: 50493f2aa4dSMichal Nazarewicz case FUNCTIONFS_RESUME: 50593f2aa4dSMichal Nazarewicz printf("Event %s\n", names[event->type]); 50693f2aa4dSMichal Nazarewicz if (event->type == FUNCTIONFS_SETUP) 50793f2aa4dSMichal Nazarewicz handle_setup(&event->u.setup); 50893f2aa4dSMichal Nazarewicz break; 50993f2aa4dSMichal Nazarewicz 51093f2aa4dSMichal Nazarewicz default: 51193f2aa4dSMichal Nazarewicz printf("Event %03u (unknown)\n", event->type); 51293f2aa4dSMichal Nazarewicz } 51393f2aa4dSMichal Nazarewicz 51493f2aa4dSMichal Nazarewicz return nbytes; 51593f2aa4dSMichal Nazarewicz } 51693f2aa4dSMichal Nazarewicz 51793f2aa4dSMichal Nazarewicz static void ep0_init(struct thread *t) 51893f2aa4dSMichal Nazarewicz { 51993f2aa4dSMichal Nazarewicz ssize_t ret; 52093f2aa4dSMichal Nazarewicz 52193f2aa4dSMichal Nazarewicz info("%s: writing descriptors\n", t->filename); 52293f2aa4dSMichal Nazarewicz ret = write(t->fd, &descriptors, sizeof descriptors); 52393f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: descriptors", t->filename); 52493f2aa4dSMichal Nazarewicz 52593f2aa4dSMichal Nazarewicz info("%s: writing strings\n", t->filename); 52693f2aa4dSMichal Nazarewicz ret = write(t->fd, &strings, sizeof strings); 52793f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: strings", t->filename); 52893f2aa4dSMichal Nazarewicz } 52993f2aa4dSMichal Nazarewicz 53093f2aa4dSMichal Nazarewicz 53193f2aa4dSMichal Nazarewicz /******************** Main **************************************************/ 53293f2aa4dSMichal Nazarewicz 53393f2aa4dSMichal Nazarewicz int main(void) 53493f2aa4dSMichal Nazarewicz { 53593f2aa4dSMichal Nazarewicz unsigned i; 53693f2aa4dSMichal Nazarewicz 53793f2aa4dSMichal Nazarewicz /* XXX TODO: Argument parsing missing */ 53893f2aa4dSMichal Nazarewicz 53993f2aa4dSMichal Nazarewicz init_thread(threads); 54093f2aa4dSMichal Nazarewicz ep0_init(threads); 54193f2aa4dSMichal Nazarewicz 54293f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 54393f2aa4dSMichal Nazarewicz init_thread(threads + i); 54493f2aa4dSMichal Nazarewicz 54593f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 54693f2aa4dSMichal Nazarewicz start_thread(threads + i); 54793f2aa4dSMichal Nazarewicz 54893f2aa4dSMichal Nazarewicz start_thread_helper(threads); 54993f2aa4dSMichal Nazarewicz 55093f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 55193f2aa4dSMichal Nazarewicz join_thread(threads + i); 55293f2aa4dSMichal Nazarewicz 55393f2aa4dSMichal Nazarewicz return 0; 55493f2aa4dSMichal Nazarewicz } 555