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