193f2aa4dSMichal Nazarewicz /* 251c208c7SMichal Nazarewicz * ffs-test.c -- user mode filesystem api for usb composite function 393f2aa4dSMichal Nazarewicz * 493f2aa4dSMichal Nazarewicz * Copyright (C) 2010 Samsung Electronics 554b8360fSMichal Nazarewicz * Author: Michal Nazarewicz <mina86@mina86.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> 32*b9a42746SMichal Nazarewicz #include <stdbool.h> 3393f2aa4dSMichal Nazarewicz #include <stdio.h> 3493f2aa4dSMichal Nazarewicz #include <stdlib.h> 3593f2aa4dSMichal Nazarewicz #include <string.h> 3693f2aa4dSMichal Nazarewicz #include <sys/ioctl.h> 3793f2aa4dSMichal Nazarewicz #include <sys/stat.h> 3893f2aa4dSMichal Nazarewicz #include <sys/types.h> 3993f2aa4dSMichal Nazarewicz #include <unistd.h> 4024fa9a9dSMatt Fleming #include <tools/le_byteshift.h> 4193f2aa4dSMichal Nazarewicz 42a0f11aceSMaxin B. John #include "../../include/uapi/linux/usb/functionfs.h" 4393f2aa4dSMichal Nazarewicz 4493f2aa4dSMichal Nazarewicz 4593f2aa4dSMichal Nazarewicz /******************** Little Endian Handling ********************************/ 4693f2aa4dSMichal Nazarewicz 4793f2aa4dSMichal Nazarewicz #define cpu_to_le16(x) htole16(x) 4893f2aa4dSMichal Nazarewicz #define cpu_to_le32(x) htole32(x) 4993f2aa4dSMichal Nazarewicz #define le32_to_cpu(x) le32toh(x) 5093f2aa4dSMichal Nazarewicz #define le16_to_cpu(x) le16toh(x) 5193f2aa4dSMichal Nazarewicz 5293f2aa4dSMichal Nazarewicz 5393f2aa4dSMichal Nazarewicz /******************** Messages and Errors ***********************************/ 5493f2aa4dSMichal Nazarewicz 5593f2aa4dSMichal Nazarewicz static const char argv0[] = "ffs-test"; 5693f2aa4dSMichal Nazarewicz 5793f2aa4dSMichal Nazarewicz static unsigned verbosity = 7; 5893f2aa4dSMichal Nazarewicz 5993f2aa4dSMichal Nazarewicz static void _msg(unsigned level, const char *fmt, ...) 6093f2aa4dSMichal Nazarewicz { 6193f2aa4dSMichal Nazarewicz if (level < 2) 6293f2aa4dSMichal Nazarewicz level = 2; 6393f2aa4dSMichal Nazarewicz else if (level > 7) 6493f2aa4dSMichal Nazarewicz level = 7; 6593f2aa4dSMichal Nazarewicz 6693f2aa4dSMichal Nazarewicz if (level <= verbosity) { 6793f2aa4dSMichal Nazarewicz static const char levels[8][6] = { 6893f2aa4dSMichal Nazarewicz [2] = "crit:", 6993f2aa4dSMichal Nazarewicz [3] = "err: ", 7093f2aa4dSMichal Nazarewicz [4] = "warn:", 7193f2aa4dSMichal Nazarewicz [5] = "note:", 7293f2aa4dSMichal Nazarewicz [6] = "info:", 7393f2aa4dSMichal Nazarewicz [7] = "dbg: " 7493f2aa4dSMichal Nazarewicz }; 7593f2aa4dSMichal Nazarewicz 7693f2aa4dSMichal Nazarewicz int _errno = errno; 7793f2aa4dSMichal Nazarewicz va_list ap; 7893f2aa4dSMichal Nazarewicz 7993f2aa4dSMichal Nazarewicz fprintf(stderr, "%s: %s ", argv0, levels[level]); 8093f2aa4dSMichal Nazarewicz va_start(ap, fmt); 8193f2aa4dSMichal Nazarewicz vfprintf(stderr, fmt, ap); 8293f2aa4dSMichal Nazarewicz va_end(ap); 8393f2aa4dSMichal Nazarewicz 8493f2aa4dSMichal Nazarewicz if (fmt[strlen(fmt) - 1] != '\n') { 8593f2aa4dSMichal Nazarewicz char buffer[128]; 8693f2aa4dSMichal Nazarewicz strerror_r(_errno, buffer, sizeof buffer); 8793f2aa4dSMichal Nazarewicz fprintf(stderr, ": (-%d) %s\n", _errno, buffer); 8893f2aa4dSMichal Nazarewicz } 8993f2aa4dSMichal Nazarewicz 9093f2aa4dSMichal Nazarewicz fflush(stderr); 9193f2aa4dSMichal Nazarewicz } 9293f2aa4dSMichal Nazarewicz } 9393f2aa4dSMichal Nazarewicz 9493f2aa4dSMichal Nazarewicz #define die(...) (_msg(2, __VA_ARGS__), exit(1)) 9593f2aa4dSMichal Nazarewicz #define err(...) _msg(3, __VA_ARGS__) 9693f2aa4dSMichal Nazarewicz #define warn(...) _msg(4, __VA_ARGS__) 9793f2aa4dSMichal Nazarewicz #define note(...) _msg(5, __VA_ARGS__) 9893f2aa4dSMichal Nazarewicz #define info(...) _msg(6, __VA_ARGS__) 9993f2aa4dSMichal Nazarewicz #define debug(...) _msg(7, __VA_ARGS__) 10093f2aa4dSMichal Nazarewicz 10193f2aa4dSMichal Nazarewicz #define die_on(cond, ...) do { \ 10293f2aa4dSMichal Nazarewicz if (cond) \ 10393f2aa4dSMichal Nazarewicz die(__VA_ARGS__); \ 10493f2aa4dSMichal Nazarewicz } while (0) 10593f2aa4dSMichal Nazarewicz 10693f2aa4dSMichal Nazarewicz 10793f2aa4dSMichal Nazarewicz /******************** Descriptors and Strings *******************************/ 10893f2aa4dSMichal Nazarewicz 10993f2aa4dSMichal Nazarewicz static const struct { 11051c208c7SMichal Nazarewicz struct usb_functionfs_descs_head_v2 header; 11151c208c7SMichal Nazarewicz __le32 fs_count; 11251c208c7SMichal Nazarewicz __le32 hs_count; 11393f2aa4dSMichal Nazarewicz struct { 11493f2aa4dSMichal Nazarewicz struct usb_interface_descriptor intf; 11593f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio sink; 11693f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio source; 11793f2aa4dSMichal Nazarewicz } __attribute__((packed)) fs_descs, hs_descs; 11893f2aa4dSMichal Nazarewicz } __attribute__((packed)) descriptors = { 11993f2aa4dSMichal Nazarewicz .header = { 12051c208c7SMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), 12151c208c7SMichal Nazarewicz .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | 12251c208c7SMichal Nazarewicz FUNCTIONFS_HAS_HS_DESC), 12393f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof descriptors), 12493f2aa4dSMichal Nazarewicz }, 12551c208c7SMichal Nazarewicz .fs_count = cpu_to_le32(3), 12693f2aa4dSMichal Nazarewicz .fs_descs = { 12793f2aa4dSMichal Nazarewicz .intf = { 12893f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 12993f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 13093f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 13193f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 13293f2aa4dSMichal Nazarewicz .iInterface = 1, 13393f2aa4dSMichal Nazarewicz }, 13493f2aa4dSMichal Nazarewicz .sink = { 13593f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.sink, 13693f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 13793f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 13893f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 13993f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 14093f2aa4dSMichal Nazarewicz }, 14193f2aa4dSMichal Nazarewicz .source = { 14293f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.source, 14393f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 14493f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 14593f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 14693f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 14793f2aa4dSMichal Nazarewicz }, 14893f2aa4dSMichal Nazarewicz }, 14951c208c7SMichal Nazarewicz .hs_count = cpu_to_le32(3), 15093f2aa4dSMichal Nazarewicz .hs_descs = { 15193f2aa4dSMichal Nazarewicz .intf = { 15293f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 15393f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 15493f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 15593f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 15693f2aa4dSMichal Nazarewicz .iInterface = 1, 15793f2aa4dSMichal Nazarewicz }, 15893f2aa4dSMichal Nazarewicz .sink = { 15993f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.sink, 16093f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 16193f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 16293f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 16393f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512), 16493f2aa4dSMichal Nazarewicz }, 16593f2aa4dSMichal Nazarewicz .source = { 16693f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.source, 16793f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 16893f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 16993f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 17093f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512), 17193f2aa4dSMichal Nazarewicz .bInterval = 1, /* NAK every 1 uframe */ 17293f2aa4dSMichal Nazarewicz }, 17393f2aa4dSMichal Nazarewicz }, 17493f2aa4dSMichal Nazarewicz }; 17593f2aa4dSMichal Nazarewicz 176*b9a42746SMichal Nazarewicz static size_t descs_to_legacy(void **legacy, const void *descriptors_v2) 177*b9a42746SMichal Nazarewicz { 178*b9a42746SMichal Nazarewicz const unsigned char *descs_end, *descs_start; 179*b9a42746SMichal Nazarewicz __u32 length, fs_count = 0, hs_count = 0, count; 180*b9a42746SMichal Nazarewicz 181*b9a42746SMichal Nazarewicz /* Read v2 header */ 182*b9a42746SMichal Nazarewicz { 183*b9a42746SMichal Nazarewicz const struct { 184*b9a42746SMichal Nazarewicz const struct usb_functionfs_descs_head_v2 header; 185*b9a42746SMichal Nazarewicz const __le32 counts[]; 186*b9a42746SMichal Nazarewicz } __attribute__((packed)) *const in = descriptors_v2; 187*b9a42746SMichal Nazarewicz const __le32 *counts = in->counts; 188*b9a42746SMichal Nazarewicz __u32 flags; 189*b9a42746SMichal Nazarewicz 190*b9a42746SMichal Nazarewicz if (le32_to_cpu(in->header.magic) != 191*b9a42746SMichal Nazarewicz FUNCTIONFS_DESCRIPTORS_MAGIC_V2) 192*b9a42746SMichal Nazarewicz return 0; 193*b9a42746SMichal Nazarewicz length = le32_to_cpu(in->header.length); 194*b9a42746SMichal Nazarewicz if (length <= sizeof in->header) 195*b9a42746SMichal Nazarewicz return 0; 196*b9a42746SMichal Nazarewicz length -= sizeof in->header; 197*b9a42746SMichal Nazarewicz flags = le32_to_cpu(in->header.flags); 198*b9a42746SMichal Nazarewicz if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | 199*b9a42746SMichal Nazarewicz FUNCTIONFS_HAS_SS_DESC)) 200*b9a42746SMichal Nazarewicz return 0; 201*b9a42746SMichal Nazarewicz 202*b9a42746SMichal Nazarewicz #define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \ 203*b9a42746SMichal Nazarewicz if (!(flags & (flg))) \ 204*b9a42746SMichal Nazarewicz break; \ 205*b9a42746SMichal Nazarewicz if (length < 4) \ 206*b9a42746SMichal Nazarewicz return 0; \ 207*b9a42746SMichal Nazarewicz ret = le32_to_cpu(*counts); \ 208*b9a42746SMichal Nazarewicz length -= 4; \ 209*b9a42746SMichal Nazarewicz ++counts; \ 210*b9a42746SMichal Nazarewicz } while (0) 211*b9a42746SMichal Nazarewicz 212*b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC); 213*b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC); 214*b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC); 215*b9a42746SMichal Nazarewicz 216*b9a42746SMichal Nazarewicz count = fs_count + hs_count; 217*b9a42746SMichal Nazarewicz if (!count) 218*b9a42746SMichal Nazarewicz return 0; 219*b9a42746SMichal Nazarewicz descs_start = (const void *)counts; 220*b9a42746SMichal Nazarewicz 221*b9a42746SMichal Nazarewicz #undef GET_NEXT_COUNT_IF_FLAG 222*b9a42746SMichal Nazarewicz } 223*b9a42746SMichal Nazarewicz 224*b9a42746SMichal Nazarewicz /* 225*b9a42746SMichal Nazarewicz * Find the end of FS and HS USB descriptors. SS descriptors 226*b9a42746SMichal Nazarewicz * are ignored since legacy format does not support them. 227*b9a42746SMichal Nazarewicz */ 228*b9a42746SMichal Nazarewicz descs_end = descs_start; 229*b9a42746SMichal Nazarewicz do { 230*b9a42746SMichal Nazarewicz if (length < *descs_end) 231*b9a42746SMichal Nazarewicz return 0; 232*b9a42746SMichal Nazarewicz length -= *descs_end; 233*b9a42746SMichal Nazarewicz descs_end += *descs_end; 234*b9a42746SMichal Nazarewicz } while (--count); 235*b9a42746SMichal Nazarewicz 236*b9a42746SMichal Nazarewicz /* Allocate legacy descriptors and copy the data. */ 237*b9a42746SMichal Nazarewicz { 238*b9a42746SMichal Nazarewicz #pragma GCC diagnostic push 239*b9a42746SMichal Nazarewicz #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 240*b9a42746SMichal Nazarewicz struct { 241*b9a42746SMichal Nazarewicz struct usb_functionfs_descs_head header; 242*b9a42746SMichal Nazarewicz __u8 descriptors[]; 243*b9a42746SMichal Nazarewicz } __attribute__((packed)) *out; 244*b9a42746SMichal Nazarewicz #pragma GCC diagnostic pop 245*b9a42746SMichal Nazarewicz 246*b9a42746SMichal Nazarewicz length = sizeof out->header + (descs_end - descs_start); 247*b9a42746SMichal Nazarewicz out = malloc(length); 248*b9a42746SMichal Nazarewicz out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); 249*b9a42746SMichal Nazarewicz out->header.length = cpu_to_le32(length); 250*b9a42746SMichal Nazarewicz out->header.fs_count = cpu_to_le32(fs_count); 251*b9a42746SMichal Nazarewicz out->header.hs_count = cpu_to_le32(hs_count); 252*b9a42746SMichal Nazarewicz memcpy(out->descriptors, descs_start, descs_end - descs_start); 253*b9a42746SMichal Nazarewicz *legacy = out; 254*b9a42746SMichal Nazarewicz } 255*b9a42746SMichal Nazarewicz 256*b9a42746SMichal Nazarewicz return length; 257*b9a42746SMichal Nazarewicz } 258*b9a42746SMichal Nazarewicz 25993f2aa4dSMichal Nazarewicz 26093f2aa4dSMichal Nazarewicz #define STR_INTERFACE_ "Source/Sink" 26193f2aa4dSMichal Nazarewicz 26293f2aa4dSMichal Nazarewicz static const struct { 26393f2aa4dSMichal Nazarewicz struct usb_functionfs_strings_head header; 26493f2aa4dSMichal Nazarewicz struct { 26593f2aa4dSMichal Nazarewicz __le16 code; 26693f2aa4dSMichal Nazarewicz const char str1[sizeof STR_INTERFACE_]; 26793f2aa4dSMichal Nazarewicz } __attribute__((packed)) lang0; 26893f2aa4dSMichal Nazarewicz } __attribute__((packed)) strings = { 26993f2aa4dSMichal Nazarewicz .header = { 27093f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), 27193f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof strings), 27293f2aa4dSMichal Nazarewicz .str_count = cpu_to_le32(1), 27393f2aa4dSMichal Nazarewicz .lang_count = cpu_to_le32(1), 27493f2aa4dSMichal Nazarewicz }, 27593f2aa4dSMichal Nazarewicz .lang0 = { 27693f2aa4dSMichal Nazarewicz cpu_to_le16(0x0409), /* en-us */ 27793f2aa4dSMichal Nazarewicz STR_INTERFACE_, 27893f2aa4dSMichal Nazarewicz }, 27993f2aa4dSMichal Nazarewicz }; 28093f2aa4dSMichal Nazarewicz 28193f2aa4dSMichal Nazarewicz #define STR_INTERFACE strings.lang0.str1 28293f2aa4dSMichal Nazarewicz 28393f2aa4dSMichal Nazarewicz 28493f2aa4dSMichal Nazarewicz /******************** Files and Threads Handling ****************************/ 28593f2aa4dSMichal Nazarewicz 28693f2aa4dSMichal Nazarewicz struct thread; 28793f2aa4dSMichal Nazarewicz 28893f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes); 28993f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes); 29093f2aa4dSMichal Nazarewicz static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes); 29193f2aa4dSMichal Nazarewicz static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes); 29293f2aa4dSMichal Nazarewicz static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes); 29393f2aa4dSMichal Nazarewicz 29493f2aa4dSMichal Nazarewicz 29593f2aa4dSMichal Nazarewicz static struct thread { 29693f2aa4dSMichal Nazarewicz const char *const filename; 29793f2aa4dSMichal Nazarewicz size_t buf_size; 29893f2aa4dSMichal Nazarewicz 29993f2aa4dSMichal Nazarewicz ssize_t (*in)(struct thread *, void *, size_t); 30093f2aa4dSMichal Nazarewicz const char *const in_name; 30193f2aa4dSMichal Nazarewicz 30293f2aa4dSMichal Nazarewicz ssize_t (*out)(struct thread *, const void *, size_t); 30393f2aa4dSMichal Nazarewicz const char *const out_name; 30493f2aa4dSMichal Nazarewicz 30593f2aa4dSMichal Nazarewicz int fd; 30693f2aa4dSMichal Nazarewicz pthread_t id; 30793f2aa4dSMichal Nazarewicz void *buf; 30893f2aa4dSMichal Nazarewicz ssize_t status; 30993f2aa4dSMichal Nazarewicz } threads[] = { 31093f2aa4dSMichal Nazarewicz { 31193f2aa4dSMichal Nazarewicz "ep0", 4 * sizeof(struct usb_functionfs_event), 31293f2aa4dSMichal Nazarewicz read_wrap, NULL, 31393f2aa4dSMichal Nazarewicz ep0_consume, "<consume>", 31493f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 31593f2aa4dSMichal Nazarewicz }, 31693f2aa4dSMichal Nazarewicz { 31793f2aa4dSMichal Nazarewicz "ep1", 8 * 1024, 31893f2aa4dSMichal Nazarewicz fill_in_buf, "<in>", 31993f2aa4dSMichal Nazarewicz write_wrap, NULL, 32093f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 32193f2aa4dSMichal Nazarewicz }, 32293f2aa4dSMichal Nazarewicz { 32393f2aa4dSMichal Nazarewicz "ep2", 8 * 1024, 32493f2aa4dSMichal Nazarewicz read_wrap, NULL, 32593f2aa4dSMichal Nazarewicz empty_out_buf, "<out>", 32693f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 32793f2aa4dSMichal Nazarewicz }, 32893f2aa4dSMichal Nazarewicz }; 32993f2aa4dSMichal Nazarewicz 33093f2aa4dSMichal Nazarewicz 33193f2aa4dSMichal Nazarewicz static void init_thread(struct thread *t) 33293f2aa4dSMichal Nazarewicz { 33393f2aa4dSMichal Nazarewicz t->buf = malloc(t->buf_size); 33493f2aa4dSMichal Nazarewicz die_on(!t->buf, "malloc"); 33593f2aa4dSMichal Nazarewicz 33693f2aa4dSMichal Nazarewicz t->fd = open(t->filename, O_RDWR); 33793f2aa4dSMichal Nazarewicz die_on(t->fd < 0, "%s", t->filename); 33893f2aa4dSMichal Nazarewicz } 33993f2aa4dSMichal Nazarewicz 34093f2aa4dSMichal Nazarewicz static void cleanup_thread(void *arg) 34193f2aa4dSMichal Nazarewicz { 34293f2aa4dSMichal Nazarewicz struct thread *t = arg; 34393f2aa4dSMichal Nazarewicz int ret, fd; 34493f2aa4dSMichal Nazarewicz 34593f2aa4dSMichal Nazarewicz fd = t->fd; 34693f2aa4dSMichal Nazarewicz if (t->fd < 0) 34793f2aa4dSMichal Nazarewicz return; 34893f2aa4dSMichal Nazarewicz t->fd = -1; 34993f2aa4dSMichal Nazarewicz 35093f2aa4dSMichal Nazarewicz /* test the FIFO ioctls (non-ep0 code paths) */ 35193f2aa4dSMichal Nazarewicz if (t != threads) { 35293f2aa4dSMichal Nazarewicz ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS); 35393f2aa4dSMichal Nazarewicz if (ret < 0) { 35493f2aa4dSMichal Nazarewicz /* ENODEV reported after disconnect */ 35593f2aa4dSMichal Nazarewicz if (errno != ENODEV) 35693f2aa4dSMichal Nazarewicz err("%s: get fifo status", t->filename); 35793f2aa4dSMichal Nazarewicz } else if (ret) { 35893f2aa4dSMichal Nazarewicz warn("%s: unclaimed = %d\n", t->filename, ret); 35993f2aa4dSMichal Nazarewicz if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0) 36093f2aa4dSMichal Nazarewicz err("%s: fifo flush", t->filename); 36193f2aa4dSMichal Nazarewicz } 36293f2aa4dSMichal Nazarewicz } 36393f2aa4dSMichal Nazarewicz 36493f2aa4dSMichal Nazarewicz if (close(fd) < 0) 36593f2aa4dSMichal Nazarewicz err("%s: close", t->filename); 36693f2aa4dSMichal Nazarewicz 36793f2aa4dSMichal Nazarewicz free(t->buf); 36893f2aa4dSMichal Nazarewicz t->buf = NULL; 36993f2aa4dSMichal Nazarewicz } 37093f2aa4dSMichal Nazarewicz 37193f2aa4dSMichal Nazarewicz static void *start_thread_helper(void *arg) 37293f2aa4dSMichal Nazarewicz { 37393f2aa4dSMichal Nazarewicz const char *name, *op, *in_name, *out_name; 37493f2aa4dSMichal Nazarewicz struct thread *t = arg; 37593f2aa4dSMichal Nazarewicz ssize_t ret; 37693f2aa4dSMichal Nazarewicz 37793f2aa4dSMichal Nazarewicz info("%s: starts\n", t->filename); 37893f2aa4dSMichal Nazarewicz in_name = t->in_name ? t->in_name : t->filename; 37993f2aa4dSMichal Nazarewicz out_name = t->out_name ? t->out_name : t->filename; 38093f2aa4dSMichal Nazarewicz 38193f2aa4dSMichal Nazarewicz pthread_cleanup_push(cleanup_thread, arg); 38293f2aa4dSMichal Nazarewicz 38393f2aa4dSMichal Nazarewicz for (;;) { 38493f2aa4dSMichal Nazarewicz pthread_testcancel(); 38593f2aa4dSMichal Nazarewicz 38693f2aa4dSMichal Nazarewicz ret = t->in(t, t->buf, t->buf_size); 38793f2aa4dSMichal Nazarewicz if (ret > 0) { 388eb9c5836SMatthias Fend ret = t->out(t, t->buf, ret); 38993f2aa4dSMichal Nazarewicz name = out_name; 39093f2aa4dSMichal Nazarewicz op = "write"; 39193f2aa4dSMichal Nazarewicz } else { 39293f2aa4dSMichal Nazarewicz name = in_name; 39393f2aa4dSMichal Nazarewicz op = "read"; 39493f2aa4dSMichal Nazarewicz } 39593f2aa4dSMichal Nazarewicz 39693f2aa4dSMichal Nazarewicz if (ret > 0) { 39793f2aa4dSMichal Nazarewicz /* nop */ 39893f2aa4dSMichal Nazarewicz } else if (!ret) { 39993f2aa4dSMichal Nazarewicz debug("%s: %s: EOF", name, op); 40093f2aa4dSMichal Nazarewicz break; 40193f2aa4dSMichal Nazarewicz } else if (errno == EINTR || errno == EAGAIN) { 40293f2aa4dSMichal Nazarewicz debug("%s: %s", name, op); 40393f2aa4dSMichal Nazarewicz } else { 40493f2aa4dSMichal Nazarewicz warn("%s: %s", name, op); 40593f2aa4dSMichal Nazarewicz break; 40693f2aa4dSMichal Nazarewicz } 40793f2aa4dSMichal Nazarewicz } 40893f2aa4dSMichal Nazarewicz 40993f2aa4dSMichal Nazarewicz pthread_cleanup_pop(1); 41093f2aa4dSMichal Nazarewicz 41193f2aa4dSMichal Nazarewicz t->status = ret; 41293f2aa4dSMichal Nazarewicz info("%s: ends\n", t->filename); 41393f2aa4dSMichal Nazarewicz return NULL; 41493f2aa4dSMichal Nazarewicz } 41593f2aa4dSMichal Nazarewicz 41693f2aa4dSMichal Nazarewicz static void start_thread(struct thread *t) 41793f2aa4dSMichal Nazarewicz { 41893f2aa4dSMichal Nazarewicz debug("%s: starting\n", t->filename); 41993f2aa4dSMichal Nazarewicz 42093f2aa4dSMichal Nazarewicz die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0, 42193f2aa4dSMichal Nazarewicz "pthread_create(%s)", t->filename); 42293f2aa4dSMichal Nazarewicz } 42393f2aa4dSMichal Nazarewicz 42493f2aa4dSMichal Nazarewicz static void join_thread(struct thread *t) 42593f2aa4dSMichal Nazarewicz { 42693f2aa4dSMichal Nazarewicz int ret = pthread_join(t->id, NULL); 42793f2aa4dSMichal Nazarewicz 42893f2aa4dSMichal Nazarewicz if (ret < 0) 42993f2aa4dSMichal Nazarewicz err("%s: joining thread", t->filename); 43093f2aa4dSMichal Nazarewicz else 43193f2aa4dSMichal Nazarewicz debug("%s: joined\n", t->filename); 43293f2aa4dSMichal Nazarewicz } 43393f2aa4dSMichal Nazarewicz 43493f2aa4dSMichal Nazarewicz 43593f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes) 43693f2aa4dSMichal Nazarewicz { 43793f2aa4dSMichal Nazarewicz return read(t->fd, buf, nbytes); 43893f2aa4dSMichal Nazarewicz } 43993f2aa4dSMichal Nazarewicz 44093f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes) 44193f2aa4dSMichal Nazarewicz { 44293f2aa4dSMichal Nazarewicz return write(t->fd, buf, nbytes); 44393f2aa4dSMichal Nazarewicz } 44493f2aa4dSMichal Nazarewicz 44593f2aa4dSMichal Nazarewicz 44693f2aa4dSMichal Nazarewicz /******************** Empty/Fill buffer routines ****************************/ 44793f2aa4dSMichal Nazarewicz 44893f2aa4dSMichal Nazarewicz /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */ 44993f2aa4dSMichal Nazarewicz enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE }; 45093f2aa4dSMichal Nazarewicz static enum pattern pattern; 45193f2aa4dSMichal Nazarewicz 45293f2aa4dSMichal Nazarewicz static ssize_t 45393f2aa4dSMichal Nazarewicz fill_in_buf(struct thread *ignore, void *buf, size_t nbytes) 45493f2aa4dSMichal Nazarewicz { 45593f2aa4dSMichal Nazarewicz size_t i; 45693f2aa4dSMichal Nazarewicz __u8 *p; 45793f2aa4dSMichal Nazarewicz 45893f2aa4dSMichal Nazarewicz (void)ignore; 45993f2aa4dSMichal Nazarewicz 46093f2aa4dSMichal Nazarewicz switch (pattern) { 46193f2aa4dSMichal Nazarewicz case PAT_ZERO: 46293f2aa4dSMichal Nazarewicz memset(buf, 0, nbytes); 46393f2aa4dSMichal Nazarewicz break; 46493f2aa4dSMichal Nazarewicz 46593f2aa4dSMichal Nazarewicz case PAT_SEQ: 46693f2aa4dSMichal Nazarewicz for (p = buf, i = 0; i < nbytes; ++i, ++p) 46793f2aa4dSMichal Nazarewicz *p = i % 63; 46893f2aa4dSMichal Nazarewicz break; 46993f2aa4dSMichal Nazarewicz 47093f2aa4dSMichal Nazarewicz case PAT_PIPE: 47193f2aa4dSMichal Nazarewicz return fread(buf, 1, nbytes, stdin); 47293f2aa4dSMichal Nazarewicz } 47393f2aa4dSMichal Nazarewicz 47493f2aa4dSMichal Nazarewicz return nbytes; 47593f2aa4dSMichal Nazarewicz } 47693f2aa4dSMichal Nazarewicz 47793f2aa4dSMichal Nazarewicz static ssize_t 47893f2aa4dSMichal Nazarewicz empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes) 47993f2aa4dSMichal Nazarewicz { 48093f2aa4dSMichal Nazarewicz const __u8 *p; 48193f2aa4dSMichal Nazarewicz __u8 expected; 48293f2aa4dSMichal Nazarewicz ssize_t ret; 48393f2aa4dSMichal Nazarewicz size_t len; 48493f2aa4dSMichal Nazarewicz 48593f2aa4dSMichal Nazarewicz (void)ignore; 48693f2aa4dSMichal Nazarewicz 48793f2aa4dSMichal Nazarewicz switch (pattern) { 48893f2aa4dSMichal Nazarewicz case PAT_ZERO: 48993f2aa4dSMichal Nazarewicz expected = 0; 49093f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 49193f2aa4dSMichal Nazarewicz if (*p) 49293f2aa4dSMichal Nazarewicz goto invalid; 49393f2aa4dSMichal Nazarewicz break; 49493f2aa4dSMichal Nazarewicz 49593f2aa4dSMichal Nazarewicz case PAT_SEQ: 49693f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 49793f2aa4dSMichal Nazarewicz if (*p != len % 63) { 49893f2aa4dSMichal Nazarewicz expected = len % 63; 49993f2aa4dSMichal Nazarewicz goto invalid; 50093f2aa4dSMichal Nazarewicz } 50193f2aa4dSMichal Nazarewicz break; 50293f2aa4dSMichal Nazarewicz 50393f2aa4dSMichal Nazarewicz case PAT_PIPE: 50493f2aa4dSMichal Nazarewicz ret = fwrite(buf, nbytes, 1, stdout); 50593f2aa4dSMichal Nazarewicz if (ret > 0) 50693f2aa4dSMichal Nazarewicz fflush(stdout); 50793f2aa4dSMichal Nazarewicz break; 50893f2aa4dSMichal Nazarewicz 50993f2aa4dSMichal Nazarewicz invalid: 51093f2aa4dSMichal Nazarewicz err("bad OUT byte %zd, expected %02x got %02x\n", 51193f2aa4dSMichal Nazarewicz len, expected, *p); 51293f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) { 51393f2aa4dSMichal Nazarewicz if (0 == (len % 32)) 514d105e74eSDavidlohr Bueso fprintf(stderr, "%4zd:", len); 51593f2aa4dSMichal Nazarewicz fprintf(stderr, " %02x", *p); 51693f2aa4dSMichal Nazarewicz if (31 == (len % 32)) 51793f2aa4dSMichal Nazarewicz fprintf(stderr, "\n"); 51893f2aa4dSMichal Nazarewicz } 51993f2aa4dSMichal Nazarewicz fflush(stderr); 52093f2aa4dSMichal Nazarewicz errno = EILSEQ; 52193f2aa4dSMichal Nazarewicz return -1; 52293f2aa4dSMichal Nazarewicz } 52393f2aa4dSMichal Nazarewicz 52493f2aa4dSMichal Nazarewicz return len; 52593f2aa4dSMichal Nazarewicz } 52693f2aa4dSMichal Nazarewicz 52793f2aa4dSMichal Nazarewicz 52893f2aa4dSMichal Nazarewicz /******************** Endpoints routines ************************************/ 52993f2aa4dSMichal Nazarewicz 53093f2aa4dSMichal Nazarewicz static void handle_setup(const struct usb_ctrlrequest *setup) 53193f2aa4dSMichal Nazarewicz { 53293f2aa4dSMichal Nazarewicz printf("bRequestType = %d\n", setup->bRequestType); 53393f2aa4dSMichal Nazarewicz printf("bRequest = %d\n", setup->bRequest); 53493f2aa4dSMichal Nazarewicz printf("wValue = %d\n", le16_to_cpu(setup->wValue)); 53593f2aa4dSMichal Nazarewicz printf("wIndex = %d\n", le16_to_cpu(setup->wIndex)); 53693f2aa4dSMichal Nazarewicz printf("wLength = %d\n", le16_to_cpu(setup->wLength)); 53793f2aa4dSMichal Nazarewicz } 53893f2aa4dSMichal Nazarewicz 53993f2aa4dSMichal Nazarewicz static ssize_t 54093f2aa4dSMichal Nazarewicz ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) 54193f2aa4dSMichal Nazarewicz { 54293f2aa4dSMichal Nazarewicz static const char *const names[] = { 54393f2aa4dSMichal Nazarewicz [FUNCTIONFS_BIND] = "BIND", 54493f2aa4dSMichal Nazarewicz [FUNCTIONFS_UNBIND] = "UNBIND", 54593f2aa4dSMichal Nazarewicz [FUNCTIONFS_ENABLE] = "ENABLE", 54693f2aa4dSMichal Nazarewicz [FUNCTIONFS_DISABLE] = "DISABLE", 54793f2aa4dSMichal Nazarewicz [FUNCTIONFS_SETUP] = "SETUP", 54893f2aa4dSMichal Nazarewicz [FUNCTIONFS_SUSPEND] = "SUSPEND", 54993f2aa4dSMichal Nazarewicz [FUNCTIONFS_RESUME] = "RESUME", 55093f2aa4dSMichal Nazarewicz }; 55193f2aa4dSMichal Nazarewicz 55293f2aa4dSMichal Nazarewicz const struct usb_functionfs_event *event = buf; 55393f2aa4dSMichal Nazarewicz size_t n; 55493f2aa4dSMichal Nazarewicz 55593f2aa4dSMichal Nazarewicz (void)ignore; 55693f2aa4dSMichal Nazarewicz 55793f2aa4dSMichal Nazarewicz for (n = nbytes / sizeof *event; n; --n, ++event) 55893f2aa4dSMichal Nazarewicz switch (event->type) { 55993f2aa4dSMichal Nazarewicz case FUNCTIONFS_BIND: 56093f2aa4dSMichal Nazarewicz case FUNCTIONFS_UNBIND: 56193f2aa4dSMichal Nazarewicz case FUNCTIONFS_ENABLE: 56293f2aa4dSMichal Nazarewicz case FUNCTIONFS_DISABLE: 56393f2aa4dSMichal Nazarewicz case FUNCTIONFS_SETUP: 56493f2aa4dSMichal Nazarewicz case FUNCTIONFS_SUSPEND: 56593f2aa4dSMichal Nazarewicz case FUNCTIONFS_RESUME: 56693f2aa4dSMichal Nazarewicz printf("Event %s\n", names[event->type]); 56793f2aa4dSMichal Nazarewicz if (event->type == FUNCTIONFS_SETUP) 56893f2aa4dSMichal Nazarewicz handle_setup(&event->u.setup); 56993f2aa4dSMichal Nazarewicz break; 57093f2aa4dSMichal Nazarewicz 57193f2aa4dSMichal Nazarewicz default: 57293f2aa4dSMichal Nazarewicz printf("Event %03u (unknown)\n", event->type); 57393f2aa4dSMichal Nazarewicz } 57493f2aa4dSMichal Nazarewicz 57593f2aa4dSMichal Nazarewicz return nbytes; 57693f2aa4dSMichal Nazarewicz } 57793f2aa4dSMichal Nazarewicz 578*b9a42746SMichal Nazarewicz static void ep0_init(struct thread *t, bool legacy_descriptors) 57993f2aa4dSMichal Nazarewicz { 580*b9a42746SMichal Nazarewicz void *legacy; 58193f2aa4dSMichal Nazarewicz ssize_t ret; 582*b9a42746SMichal Nazarewicz size_t len; 58393f2aa4dSMichal Nazarewicz 584*b9a42746SMichal Nazarewicz if (legacy_descriptors) { 58593f2aa4dSMichal Nazarewicz info("%s: writing descriptors\n", t->filename); 586*b9a42746SMichal Nazarewicz goto legacy; 587*b9a42746SMichal Nazarewicz } 588*b9a42746SMichal Nazarewicz 589*b9a42746SMichal Nazarewicz info("%s: writing descriptors (in v2 format)\n", t->filename); 59093f2aa4dSMichal Nazarewicz ret = write(t->fd, &descriptors, sizeof descriptors); 591*b9a42746SMichal Nazarewicz 592*b9a42746SMichal Nazarewicz if (ret < 0 && errno == EINVAL) { 593*b9a42746SMichal Nazarewicz warn("%s: new format rejected, trying legacy\n", t->filename); 594*b9a42746SMichal Nazarewicz legacy: 595*b9a42746SMichal Nazarewicz len = descs_to_legacy(&legacy, &descriptors); 596*b9a42746SMichal Nazarewicz if (len) { 597*b9a42746SMichal Nazarewicz ret = write(t->fd, legacy, len); 598*b9a42746SMichal Nazarewicz free(legacy); 599*b9a42746SMichal Nazarewicz } 600*b9a42746SMichal Nazarewicz } 60193f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: descriptors", t->filename); 60293f2aa4dSMichal Nazarewicz 60393f2aa4dSMichal Nazarewicz info("%s: writing strings\n", t->filename); 60493f2aa4dSMichal Nazarewicz ret = write(t->fd, &strings, sizeof strings); 60593f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: strings", t->filename); 60693f2aa4dSMichal Nazarewicz } 60793f2aa4dSMichal Nazarewicz 60893f2aa4dSMichal Nazarewicz 60993f2aa4dSMichal Nazarewicz /******************** Main **************************************************/ 61093f2aa4dSMichal Nazarewicz 611*b9a42746SMichal Nazarewicz int main(int argc, char **argv) 61293f2aa4dSMichal Nazarewicz { 613*b9a42746SMichal Nazarewicz bool legacy_descriptors; 61493f2aa4dSMichal Nazarewicz unsigned i; 61593f2aa4dSMichal Nazarewicz 616*b9a42746SMichal Nazarewicz legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l"); 61793f2aa4dSMichal Nazarewicz 61893f2aa4dSMichal Nazarewicz init_thread(threads); 619*b9a42746SMichal Nazarewicz ep0_init(threads, legacy_descriptors); 62093f2aa4dSMichal Nazarewicz 62193f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 62293f2aa4dSMichal Nazarewicz init_thread(threads + i); 62393f2aa4dSMichal Nazarewicz 62493f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 62593f2aa4dSMichal Nazarewicz start_thread(threads + i); 62693f2aa4dSMichal Nazarewicz 62793f2aa4dSMichal Nazarewicz start_thread_helper(threads); 62893f2aa4dSMichal Nazarewicz 62993f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 63093f2aa4dSMichal Nazarewicz join_thread(threads + i); 63193f2aa4dSMichal Nazarewicz 63293f2aa4dSMichal Nazarewicz return 0; 63393f2aa4dSMichal Nazarewicz } 634