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 255abb9b91SFelipe Balbi #define _DEFAULT_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> 32b9a42746SMichal 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 47*a2b22dddSPeter Senna Tschudin /* 48*a2b22dddSPeter Senna Tschudin * cpu_to_le16/32 are used when initializing structures, a context where a 49*a2b22dddSPeter Senna Tschudin * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way 50*a2b22dddSPeter Senna Tschudin * that allows them to be used when initializing structures. 51*a2b22dddSPeter Senna Tschudin */ 52*a2b22dddSPeter Senna Tschudin 53*a2b22dddSPeter Senna Tschudin #if __BYTE_ORDER == __LITTLE_ENDIAN 54*a2b22dddSPeter Senna Tschudin #define cpu_to_le16(x) (x) 55*a2b22dddSPeter Senna Tschudin #define cpu_to_le32(x) (x) 56*a2b22dddSPeter Senna Tschudin #else 57*a2b22dddSPeter Senna Tschudin #define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) 58*a2b22dddSPeter Senna Tschudin #define cpu_to_le32(x) \ 59*a2b22dddSPeter Senna Tschudin ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ 60*a2b22dddSPeter Senna Tschudin (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) 61*a2b22dddSPeter Senna Tschudin #endif 62*a2b22dddSPeter Senna Tschudin 6393f2aa4dSMichal Nazarewicz #define le32_to_cpu(x) le32toh(x) 6493f2aa4dSMichal Nazarewicz #define le16_to_cpu(x) le16toh(x) 6593f2aa4dSMichal Nazarewicz 6693f2aa4dSMichal Nazarewicz /******************** Messages and Errors ***********************************/ 6793f2aa4dSMichal Nazarewicz 6893f2aa4dSMichal Nazarewicz static const char argv0[] = "ffs-test"; 6993f2aa4dSMichal Nazarewicz 7093f2aa4dSMichal Nazarewicz static unsigned verbosity = 7; 7193f2aa4dSMichal Nazarewicz 7293f2aa4dSMichal Nazarewicz static void _msg(unsigned level, const char *fmt, ...) 7393f2aa4dSMichal Nazarewicz { 7493f2aa4dSMichal Nazarewicz if (level < 2) 7593f2aa4dSMichal Nazarewicz level = 2; 7693f2aa4dSMichal Nazarewicz else if (level > 7) 7793f2aa4dSMichal Nazarewicz level = 7; 7893f2aa4dSMichal Nazarewicz 7993f2aa4dSMichal Nazarewicz if (level <= verbosity) { 8093f2aa4dSMichal Nazarewicz static const char levels[8][6] = { 8193f2aa4dSMichal Nazarewicz [2] = "crit:", 8293f2aa4dSMichal Nazarewicz [3] = "err: ", 8393f2aa4dSMichal Nazarewicz [4] = "warn:", 8493f2aa4dSMichal Nazarewicz [5] = "note:", 8593f2aa4dSMichal Nazarewicz [6] = "info:", 8693f2aa4dSMichal Nazarewicz [7] = "dbg: " 8793f2aa4dSMichal Nazarewicz }; 8893f2aa4dSMichal Nazarewicz 8993f2aa4dSMichal Nazarewicz int _errno = errno; 9093f2aa4dSMichal Nazarewicz va_list ap; 9193f2aa4dSMichal Nazarewicz 9293f2aa4dSMichal Nazarewicz fprintf(stderr, "%s: %s ", argv0, levels[level]); 9393f2aa4dSMichal Nazarewicz va_start(ap, fmt); 9493f2aa4dSMichal Nazarewicz vfprintf(stderr, fmt, ap); 9593f2aa4dSMichal Nazarewicz va_end(ap); 9693f2aa4dSMichal Nazarewicz 9793f2aa4dSMichal Nazarewicz if (fmt[strlen(fmt) - 1] != '\n') { 9893f2aa4dSMichal Nazarewicz char buffer[128]; 9993f2aa4dSMichal Nazarewicz strerror_r(_errno, buffer, sizeof buffer); 10093f2aa4dSMichal Nazarewicz fprintf(stderr, ": (-%d) %s\n", _errno, buffer); 10193f2aa4dSMichal Nazarewicz } 10293f2aa4dSMichal Nazarewicz 10393f2aa4dSMichal Nazarewicz fflush(stderr); 10493f2aa4dSMichal Nazarewicz } 10593f2aa4dSMichal Nazarewicz } 10693f2aa4dSMichal Nazarewicz 10793f2aa4dSMichal Nazarewicz #define die(...) (_msg(2, __VA_ARGS__), exit(1)) 10893f2aa4dSMichal Nazarewicz #define err(...) _msg(3, __VA_ARGS__) 10993f2aa4dSMichal Nazarewicz #define warn(...) _msg(4, __VA_ARGS__) 11093f2aa4dSMichal Nazarewicz #define note(...) _msg(5, __VA_ARGS__) 11193f2aa4dSMichal Nazarewicz #define info(...) _msg(6, __VA_ARGS__) 11293f2aa4dSMichal Nazarewicz #define debug(...) _msg(7, __VA_ARGS__) 11393f2aa4dSMichal Nazarewicz 11493f2aa4dSMichal Nazarewicz #define die_on(cond, ...) do { \ 11593f2aa4dSMichal Nazarewicz if (cond) \ 11693f2aa4dSMichal Nazarewicz die(__VA_ARGS__); \ 11793f2aa4dSMichal Nazarewicz } while (0) 11893f2aa4dSMichal Nazarewicz 11993f2aa4dSMichal Nazarewicz 12093f2aa4dSMichal Nazarewicz /******************** Descriptors and Strings *******************************/ 12193f2aa4dSMichal Nazarewicz 12293f2aa4dSMichal Nazarewicz static const struct { 12351c208c7SMichal Nazarewicz struct usb_functionfs_descs_head_v2 header; 12451c208c7SMichal Nazarewicz __le32 fs_count; 12551c208c7SMichal Nazarewicz __le32 hs_count; 126271d2d6dSFelipe Balbi __le32 ss_count; 12793f2aa4dSMichal Nazarewicz struct { 12893f2aa4dSMichal Nazarewicz struct usb_interface_descriptor intf; 12993f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio sink; 13093f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio source; 13193f2aa4dSMichal Nazarewicz } __attribute__((packed)) fs_descs, hs_descs; 132271d2d6dSFelipe Balbi struct { 133271d2d6dSFelipe Balbi struct usb_interface_descriptor intf; 134271d2d6dSFelipe Balbi struct usb_endpoint_descriptor_no_audio sink; 135271d2d6dSFelipe Balbi struct usb_ss_ep_comp_descriptor sink_comp; 136271d2d6dSFelipe Balbi struct usb_endpoint_descriptor_no_audio source; 137271d2d6dSFelipe Balbi struct usb_ss_ep_comp_descriptor source_comp; 138271d2d6dSFelipe Balbi } ss_descs; 13993f2aa4dSMichal Nazarewicz } __attribute__((packed)) descriptors = { 14093f2aa4dSMichal Nazarewicz .header = { 14151c208c7SMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), 14251c208c7SMichal Nazarewicz .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | 143271d2d6dSFelipe Balbi FUNCTIONFS_HAS_HS_DESC | 144271d2d6dSFelipe Balbi FUNCTIONFS_HAS_SS_DESC), 14593f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof descriptors), 14693f2aa4dSMichal Nazarewicz }, 14751c208c7SMichal Nazarewicz .fs_count = cpu_to_le32(3), 14893f2aa4dSMichal Nazarewicz .fs_descs = { 14993f2aa4dSMichal Nazarewicz .intf = { 15093f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf, 15193f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE, 15293f2aa4dSMichal Nazarewicz .bNumEndpoints = 2, 15393f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 15493f2aa4dSMichal Nazarewicz .iInterface = 1, 15593f2aa4dSMichal Nazarewicz }, 15693f2aa4dSMichal Nazarewicz .sink = { 15793f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.sink, 15893f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 15993f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN, 16093f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 16193f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 16293f2aa4dSMichal Nazarewicz }, 16393f2aa4dSMichal Nazarewicz .source = { 16493f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.source, 16593f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT, 16693f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT, 16793f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK, 16893f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */ 16993f2aa4dSMichal Nazarewicz }, 17093f2aa4dSMichal Nazarewicz }, 17151c208c7SMichal Nazarewicz .hs_count = cpu_to_le32(3), 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 }, 196271d2d6dSFelipe Balbi .ss_count = cpu_to_le32(5), 197271d2d6dSFelipe Balbi .ss_descs = { 198271d2d6dSFelipe Balbi .intf = { 199271d2d6dSFelipe Balbi .bLength = sizeof descriptors.fs_descs.intf, 200271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_INTERFACE, 201271d2d6dSFelipe Balbi .bNumEndpoints = 2, 202271d2d6dSFelipe Balbi .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 203271d2d6dSFelipe Balbi .iInterface = 1, 204271d2d6dSFelipe Balbi }, 205271d2d6dSFelipe Balbi .sink = { 206271d2d6dSFelipe Balbi .bLength = sizeof descriptors.hs_descs.sink, 207271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT, 208271d2d6dSFelipe Balbi .bEndpointAddress = 1 | USB_DIR_IN, 209271d2d6dSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK, 210271d2d6dSFelipe Balbi .wMaxPacketSize = cpu_to_le16(1024), 211271d2d6dSFelipe Balbi }, 212271d2d6dSFelipe Balbi .sink_comp = { 213271d2d6dSFelipe Balbi .bLength = USB_DT_SS_EP_COMP_SIZE, 214271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 215271d2d6dSFelipe Balbi .bMaxBurst = 0, 216271d2d6dSFelipe Balbi .bmAttributes = 0, 217271d2d6dSFelipe Balbi .wBytesPerInterval = 0, 218271d2d6dSFelipe Balbi }, 219271d2d6dSFelipe Balbi .source = { 220271d2d6dSFelipe Balbi .bLength = sizeof descriptors.hs_descs.source, 221271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT, 222271d2d6dSFelipe Balbi .bEndpointAddress = 2 | USB_DIR_OUT, 223271d2d6dSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK, 224271d2d6dSFelipe Balbi .wMaxPacketSize = cpu_to_le16(1024), 225271d2d6dSFelipe Balbi .bInterval = 1, /* NAK every 1 uframe */ 226271d2d6dSFelipe Balbi }, 227271d2d6dSFelipe Balbi .source_comp = { 228271d2d6dSFelipe Balbi .bLength = USB_DT_SS_EP_COMP_SIZE, 229271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 230271d2d6dSFelipe Balbi .bMaxBurst = 0, 231271d2d6dSFelipe Balbi .bmAttributes = 0, 232271d2d6dSFelipe Balbi .wBytesPerInterval = 0, 233271d2d6dSFelipe Balbi }, 234271d2d6dSFelipe Balbi }, 23593f2aa4dSMichal Nazarewicz }; 23693f2aa4dSMichal Nazarewicz 237b9a42746SMichal Nazarewicz static size_t descs_to_legacy(void **legacy, const void *descriptors_v2) 238b9a42746SMichal Nazarewicz { 239b9a42746SMichal Nazarewicz const unsigned char *descs_end, *descs_start; 240b9a42746SMichal Nazarewicz __u32 length, fs_count = 0, hs_count = 0, count; 241b9a42746SMichal Nazarewicz 242b9a42746SMichal Nazarewicz /* Read v2 header */ 243b9a42746SMichal Nazarewicz { 244b9a42746SMichal Nazarewicz const struct { 245b9a42746SMichal Nazarewicz const struct usb_functionfs_descs_head_v2 header; 246b9a42746SMichal Nazarewicz const __le32 counts[]; 247b9a42746SMichal Nazarewicz } __attribute__((packed)) *const in = descriptors_v2; 248b9a42746SMichal Nazarewicz const __le32 *counts = in->counts; 249b9a42746SMichal Nazarewicz __u32 flags; 250b9a42746SMichal Nazarewicz 251b9a42746SMichal Nazarewicz if (le32_to_cpu(in->header.magic) != 252b9a42746SMichal Nazarewicz FUNCTIONFS_DESCRIPTORS_MAGIC_V2) 253b9a42746SMichal Nazarewicz return 0; 254b9a42746SMichal Nazarewicz length = le32_to_cpu(in->header.length); 255b9a42746SMichal Nazarewicz if (length <= sizeof in->header) 256b9a42746SMichal Nazarewicz return 0; 257b9a42746SMichal Nazarewicz length -= sizeof in->header; 258b9a42746SMichal Nazarewicz flags = le32_to_cpu(in->header.flags); 259b9a42746SMichal Nazarewicz if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | 260b9a42746SMichal Nazarewicz FUNCTIONFS_HAS_SS_DESC)) 261b9a42746SMichal Nazarewicz return 0; 262b9a42746SMichal Nazarewicz 263b9a42746SMichal Nazarewicz #define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \ 264b9a42746SMichal Nazarewicz if (!(flags & (flg))) \ 265b9a42746SMichal Nazarewicz break; \ 266b9a42746SMichal Nazarewicz if (length < 4) \ 267b9a42746SMichal Nazarewicz return 0; \ 268b9a42746SMichal Nazarewicz ret = le32_to_cpu(*counts); \ 269b9a42746SMichal Nazarewicz length -= 4; \ 270b9a42746SMichal Nazarewicz ++counts; \ 271b9a42746SMichal Nazarewicz } while (0) 272b9a42746SMichal Nazarewicz 273b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC); 274b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC); 275b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC); 276b9a42746SMichal Nazarewicz 277b9a42746SMichal Nazarewicz count = fs_count + hs_count; 278b9a42746SMichal Nazarewicz if (!count) 279b9a42746SMichal Nazarewicz return 0; 280b9a42746SMichal Nazarewicz descs_start = (const void *)counts; 281b9a42746SMichal Nazarewicz 282b9a42746SMichal Nazarewicz #undef GET_NEXT_COUNT_IF_FLAG 283b9a42746SMichal Nazarewicz } 284b9a42746SMichal Nazarewicz 285b9a42746SMichal Nazarewicz /* 286b9a42746SMichal Nazarewicz * Find the end of FS and HS USB descriptors. SS descriptors 287b9a42746SMichal Nazarewicz * are ignored since legacy format does not support them. 288b9a42746SMichal Nazarewicz */ 289b9a42746SMichal Nazarewicz descs_end = descs_start; 290b9a42746SMichal Nazarewicz do { 291b9a42746SMichal Nazarewicz if (length < *descs_end) 292b9a42746SMichal Nazarewicz return 0; 293b9a42746SMichal Nazarewicz length -= *descs_end; 294b9a42746SMichal Nazarewicz descs_end += *descs_end; 295b9a42746SMichal Nazarewicz } while (--count); 296b9a42746SMichal Nazarewicz 297b9a42746SMichal Nazarewicz /* Allocate legacy descriptors and copy the data. */ 298b9a42746SMichal Nazarewicz { 299b9a42746SMichal Nazarewicz #pragma GCC diagnostic push 300b9a42746SMichal Nazarewicz #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 301b9a42746SMichal Nazarewicz struct { 302b9a42746SMichal Nazarewicz struct usb_functionfs_descs_head header; 303b9a42746SMichal Nazarewicz __u8 descriptors[]; 304b9a42746SMichal Nazarewicz } __attribute__((packed)) *out; 305b9a42746SMichal Nazarewicz #pragma GCC diagnostic pop 306b9a42746SMichal Nazarewicz 307b9a42746SMichal Nazarewicz length = sizeof out->header + (descs_end - descs_start); 308b9a42746SMichal Nazarewicz out = malloc(length); 309b9a42746SMichal Nazarewicz out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); 310b9a42746SMichal Nazarewicz out->header.length = cpu_to_le32(length); 311b9a42746SMichal Nazarewicz out->header.fs_count = cpu_to_le32(fs_count); 312b9a42746SMichal Nazarewicz out->header.hs_count = cpu_to_le32(hs_count); 313b9a42746SMichal Nazarewicz memcpy(out->descriptors, descs_start, descs_end - descs_start); 314b9a42746SMichal Nazarewicz *legacy = out; 315b9a42746SMichal Nazarewicz } 316b9a42746SMichal Nazarewicz 317b9a42746SMichal Nazarewicz return length; 318b9a42746SMichal Nazarewicz } 319b9a42746SMichal Nazarewicz 32093f2aa4dSMichal Nazarewicz 32193f2aa4dSMichal Nazarewicz #define STR_INTERFACE_ "Source/Sink" 32293f2aa4dSMichal Nazarewicz 32393f2aa4dSMichal Nazarewicz static const struct { 32493f2aa4dSMichal Nazarewicz struct usb_functionfs_strings_head header; 32593f2aa4dSMichal Nazarewicz struct { 32693f2aa4dSMichal Nazarewicz __le16 code; 32793f2aa4dSMichal Nazarewicz const char str1[sizeof STR_INTERFACE_]; 32893f2aa4dSMichal Nazarewicz } __attribute__((packed)) lang0; 32993f2aa4dSMichal Nazarewicz } __attribute__((packed)) strings = { 33093f2aa4dSMichal Nazarewicz .header = { 33193f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), 33293f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof strings), 33393f2aa4dSMichal Nazarewicz .str_count = cpu_to_le32(1), 33493f2aa4dSMichal Nazarewicz .lang_count = cpu_to_le32(1), 33593f2aa4dSMichal Nazarewicz }, 33693f2aa4dSMichal Nazarewicz .lang0 = { 33793f2aa4dSMichal Nazarewicz cpu_to_le16(0x0409), /* en-us */ 33893f2aa4dSMichal Nazarewicz STR_INTERFACE_, 33993f2aa4dSMichal Nazarewicz }, 34093f2aa4dSMichal Nazarewicz }; 34193f2aa4dSMichal Nazarewicz 34293f2aa4dSMichal Nazarewicz #define STR_INTERFACE strings.lang0.str1 34393f2aa4dSMichal Nazarewicz 34493f2aa4dSMichal Nazarewicz 34593f2aa4dSMichal Nazarewicz /******************** Files and Threads Handling ****************************/ 34693f2aa4dSMichal Nazarewicz 34793f2aa4dSMichal Nazarewicz struct thread; 34893f2aa4dSMichal Nazarewicz 34993f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes); 35093f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes); 35193f2aa4dSMichal Nazarewicz static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes); 35293f2aa4dSMichal Nazarewicz static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes); 35393f2aa4dSMichal Nazarewicz static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes); 35493f2aa4dSMichal Nazarewicz 35593f2aa4dSMichal Nazarewicz 35693f2aa4dSMichal Nazarewicz static struct thread { 35793f2aa4dSMichal Nazarewicz const char *const filename; 35893f2aa4dSMichal Nazarewicz size_t buf_size; 35993f2aa4dSMichal Nazarewicz 36093f2aa4dSMichal Nazarewicz ssize_t (*in)(struct thread *, void *, size_t); 36193f2aa4dSMichal Nazarewicz const char *const in_name; 36293f2aa4dSMichal Nazarewicz 36393f2aa4dSMichal Nazarewicz ssize_t (*out)(struct thread *, const void *, size_t); 36493f2aa4dSMichal Nazarewicz const char *const out_name; 36593f2aa4dSMichal Nazarewicz 36693f2aa4dSMichal Nazarewicz int fd; 36793f2aa4dSMichal Nazarewicz pthread_t id; 36893f2aa4dSMichal Nazarewicz void *buf; 36993f2aa4dSMichal Nazarewicz ssize_t status; 37093f2aa4dSMichal Nazarewicz } threads[] = { 37193f2aa4dSMichal Nazarewicz { 37293f2aa4dSMichal Nazarewicz "ep0", 4 * sizeof(struct usb_functionfs_event), 37393f2aa4dSMichal Nazarewicz read_wrap, NULL, 37493f2aa4dSMichal Nazarewicz ep0_consume, "<consume>", 37593f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 37693f2aa4dSMichal Nazarewicz }, 37793f2aa4dSMichal Nazarewicz { 37893f2aa4dSMichal Nazarewicz "ep1", 8 * 1024, 37993f2aa4dSMichal Nazarewicz fill_in_buf, "<in>", 38093f2aa4dSMichal Nazarewicz write_wrap, NULL, 38193f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 38293f2aa4dSMichal Nazarewicz }, 38393f2aa4dSMichal Nazarewicz { 38493f2aa4dSMichal Nazarewicz "ep2", 8 * 1024, 38593f2aa4dSMichal Nazarewicz read_wrap, NULL, 38693f2aa4dSMichal Nazarewicz empty_out_buf, "<out>", 38793f2aa4dSMichal Nazarewicz 0, 0, NULL, 0 38893f2aa4dSMichal Nazarewicz }, 38993f2aa4dSMichal Nazarewicz }; 39093f2aa4dSMichal Nazarewicz 39193f2aa4dSMichal Nazarewicz 39293f2aa4dSMichal Nazarewicz static void init_thread(struct thread *t) 39393f2aa4dSMichal Nazarewicz { 39493f2aa4dSMichal Nazarewicz t->buf = malloc(t->buf_size); 39593f2aa4dSMichal Nazarewicz die_on(!t->buf, "malloc"); 39693f2aa4dSMichal Nazarewicz 39793f2aa4dSMichal Nazarewicz t->fd = open(t->filename, O_RDWR); 39893f2aa4dSMichal Nazarewicz die_on(t->fd < 0, "%s", t->filename); 39993f2aa4dSMichal Nazarewicz } 40093f2aa4dSMichal Nazarewicz 40193f2aa4dSMichal Nazarewicz static void cleanup_thread(void *arg) 40293f2aa4dSMichal Nazarewicz { 40393f2aa4dSMichal Nazarewicz struct thread *t = arg; 40493f2aa4dSMichal Nazarewicz int ret, fd; 40593f2aa4dSMichal Nazarewicz 40693f2aa4dSMichal Nazarewicz fd = t->fd; 40793f2aa4dSMichal Nazarewicz if (t->fd < 0) 40893f2aa4dSMichal Nazarewicz return; 40993f2aa4dSMichal Nazarewicz t->fd = -1; 41093f2aa4dSMichal Nazarewicz 41193f2aa4dSMichal Nazarewicz /* test the FIFO ioctls (non-ep0 code paths) */ 41293f2aa4dSMichal Nazarewicz if (t != threads) { 41393f2aa4dSMichal Nazarewicz ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS); 41493f2aa4dSMichal Nazarewicz if (ret < 0) { 41593f2aa4dSMichal Nazarewicz /* ENODEV reported after disconnect */ 41693f2aa4dSMichal Nazarewicz if (errno != ENODEV) 41793f2aa4dSMichal Nazarewicz err("%s: get fifo status", t->filename); 41893f2aa4dSMichal Nazarewicz } else if (ret) { 41993f2aa4dSMichal Nazarewicz warn("%s: unclaimed = %d\n", t->filename, ret); 42093f2aa4dSMichal Nazarewicz if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0) 42193f2aa4dSMichal Nazarewicz err("%s: fifo flush", t->filename); 42293f2aa4dSMichal Nazarewicz } 42393f2aa4dSMichal Nazarewicz } 42493f2aa4dSMichal Nazarewicz 42593f2aa4dSMichal Nazarewicz if (close(fd) < 0) 42693f2aa4dSMichal Nazarewicz err("%s: close", t->filename); 42793f2aa4dSMichal Nazarewicz 42893f2aa4dSMichal Nazarewicz free(t->buf); 42993f2aa4dSMichal Nazarewicz t->buf = NULL; 43093f2aa4dSMichal Nazarewicz } 43193f2aa4dSMichal Nazarewicz 43293f2aa4dSMichal Nazarewicz static void *start_thread_helper(void *arg) 43393f2aa4dSMichal Nazarewicz { 43493f2aa4dSMichal Nazarewicz const char *name, *op, *in_name, *out_name; 43593f2aa4dSMichal Nazarewicz struct thread *t = arg; 43693f2aa4dSMichal Nazarewicz ssize_t ret; 43793f2aa4dSMichal Nazarewicz 43893f2aa4dSMichal Nazarewicz info("%s: starts\n", t->filename); 43993f2aa4dSMichal Nazarewicz in_name = t->in_name ? t->in_name : t->filename; 44093f2aa4dSMichal Nazarewicz out_name = t->out_name ? t->out_name : t->filename; 44193f2aa4dSMichal Nazarewicz 44293f2aa4dSMichal Nazarewicz pthread_cleanup_push(cleanup_thread, arg); 44393f2aa4dSMichal Nazarewicz 44493f2aa4dSMichal Nazarewicz for (;;) { 44593f2aa4dSMichal Nazarewicz pthread_testcancel(); 44693f2aa4dSMichal Nazarewicz 44793f2aa4dSMichal Nazarewicz ret = t->in(t, t->buf, t->buf_size); 44893f2aa4dSMichal Nazarewicz if (ret > 0) { 449eb9c5836SMatthias Fend ret = t->out(t, t->buf, ret); 45093f2aa4dSMichal Nazarewicz name = out_name; 45193f2aa4dSMichal Nazarewicz op = "write"; 45293f2aa4dSMichal Nazarewicz } else { 45393f2aa4dSMichal Nazarewicz name = in_name; 45493f2aa4dSMichal Nazarewicz op = "read"; 45593f2aa4dSMichal Nazarewicz } 45693f2aa4dSMichal Nazarewicz 45793f2aa4dSMichal Nazarewicz if (ret > 0) { 45893f2aa4dSMichal Nazarewicz /* nop */ 45993f2aa4dSMichal Nazarewicz } else if (!ret) { 46093f2aa4dSMichal Nazarewicz debug("%s: %s: EOF", name, op); 46193f2aa4dSMichal Nazarewicz break; 46293f2aa4dSMichal Nazarewicz } else if (errno == EINTR || errno == EAGAIN) { 46393f2aa4dSMichal Nazarewicz debug("%s: %s", name, op); 46493f2aa4dSMichal Nazarewicz } else { 46593f2aa4dSMichal Nazarewicz warn("%s: %s", name, op); 46693f2aa4dSMichal Nazarewicz break; 46793f2aa4dSMichal Nazarewicz } 46893f2aa4dSMichal Nazarewicz } 46993f2aa4dSMichal Nazarewicz 47093f2aa4dSMichal Nazarewicz pthread_cleanup_pop(1); 47193f2aa4dSMichal Nazarewicz 47293f2aa4dSMichal Nazarewicz t->status = ret; 47393f2aa4dSMichal Nazarewicz info("%s: ends\n", t->filename); 47493f2aa4dSMichal Nazarewicz return NULL; 47593f2aa4dSMichal Nazarewicz } 47693f2aa4dSMichal Nazarewicz 47793f2aa4dSMichal Nazarewicz static void start_thread(struct thread *t) 47893f2aa4dSMichal Nazarewicz { 47993f2aa4dSMichal Nazarewicz debug("%s: starting\n", t->filename); 48093f2aa4dSMichal Nazarewicz 48193f2aa4dSMichal Nazarewicz die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0, 48293f2aa4dSMichal Nazarewicz "pthread_create(%s)", t->filename); 48393f2aa4dSMichal Nazarewicz } 48493f2aa4dSMichal Nazarewicz 48593f2aa4dSMichal Nazarewicz static void join_thread(struct thread *t) 48693f2aa4dSMichal Nazarewicz { 48793f2aa4dSMichal Nazarewicz int ret = pthread_join(t->id, NULL); 48893f2aa4dSMichal Nazarewicz 48993f2aa4dSMichal Nazarewicz if (ret < 0) 49093f2aa4dSMichal Nazarewicz err("%s: joining thread", t->filename); 49193f2aa4dSMichal Nazarewicz else 49293f2aa4dSMichal Nazarewicz debug("%s: joined\n", t->filename); 49393f2aa4dSMichal Nazarewicz } 49493f2aa4dSMichal Nazarewicz 49593f2aa4dSMichal Nazarewicz 49693f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes) 49793f2aa4dSMichal Nazarewicz { 49893f2aa4dSMichal Nazarewicz return read(t->fd, buf, nbytes); 49993f2aa4dSMichal Nazarewicz } 50093f2aa4dSMichal Nazarewicz 50193f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes) 50293f2aa4dSMichal Nazarewicz { 50393f2aa4dSMichal Nazarewicz return write(t->fd, buf, nbytes); 50493f2aa4dSMichal Nazarewicz } 50593f2aa4dSMichal Nazarewicz 50693f2aa4dSMichal Nazarewicz 50793f2aa4dSMichal Nazarewicz /******************** Empty/Fill buffer routines ****************************/ 50893f2aa4dSMichal Nazarewicz 50993f2aa4dSMichal Nazarewicz /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */ 51093f2aa4dSMichal Nazarewicz enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE }; 51193f2aa4dSMichal Nazarewicz static enum pattern pattern; 51293f2aa4dSMichal Nazarewicz 51393f2aa4dSMichal Nazarewicz static ssize_t 51493f2aa4dSMichal Nazarewicz fill_in_buf(struct thread *ignore, void *buf, size_t nbytes) 51593f2aa4dSMichal Nazarewicz { 51693f2aa4dSMichal Nazarewicz size_t i; 51793f2aa4dSMichal Nazarewicz __u8 *p; 51893f2aa4dSMichal Nazarewicz 51993f2aa4dSMichal Nazarewicz (void)ignore; 52093f2aa4dSMichal Nazarewicz 52193f2aa4dSMichal Nazarewicz switch (pattern) { 52293f2aa4dSMichal Nazarewicz case PAT_ZERO: 52393f2aa4dSMichal Nazarewicz memset(buf, 0, nbytes); 52493f2aa4dSMichal Nazarewicz break; 52593f2aa4dSMichal Nazarewicz 52693f2aa4dSMichal Nazarewicz case PAT_SEQ: 52793f2aa4dSMichal Nazarewicz for (p = buf, i = 0; i < nbytes; ++i, ++p) 52893f2aa4dSMichal Nazarewicz *p = i % 63; 52993f2aa4dSMichal Nazarewicz break; 53093f2aa4dSMichal Nazarewicz 53193f2aa4dSMichal Nazarewicz case PAT_PIPE: 53293f2aa4dSMichal Nazarewicz return fread(buf, 1, nbytes, stdin); 53393f2aa4dSMichal Nazarewicz } 53493f2aa4dSMichal Nazarewicz 53593f2aa4dSMichal Nazarewicz return nbytes; 53693f2aa4dSMichal Nazarewicz } 53793f2aa4dSMichal Nazarewicz 53893f2aa4dSMichal Nazarewicz static ssize_t 53993f2aa4dSMichal Nazarewicz empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes) 54093f2aa4dSMichal Nazarewicz { 54193f2aa4dSMichal Nazarewicz const __u8 *p; 54293f2aa4dSMichal Nazarewicz __u8 expected; 54393f2aa4dSMichal Nazarewicz ssize_t ret; 54493f2aa4dSMichal Nazarewicz size_t len; 54593f2aa4dSMichal Nazarewicz 54693f2aa4dSMichal Nazarewicz (void)ignore; 54793f2aa4dSMichal Nazarewicz 54893f2aa4dSMichal Nazarewicz switch (pattern) { 54993f2aa4dSMichal Nazarewicz case PAT_ZERO: 55093f2aa4dSMichal Nazarewicz expected = 0; 55193f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 55293f2aa4dSMichal Nazarewicz if (*p) 55393f2aa4dSMichal Nazarewicz goto invalid; 55493f2aa4dSMichal Nazarewicz break; 55593f2aa4dSMichal Nazarewicz 55693f2aa4dSMichal Nazarewicz case PAT_SEQ: 55793f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) 55893f2aa4dSMichal Nazarewicz if (*p != len % 63) { 55993f2aa4dSMichal Nazarewicz expected = len % 63; 56093f2aa4dSMichal Nazarewicz goto invalid; 56193f2aa4dSMichal Nazarewicz } 56293f2aa4dSMichal Nazarewicz break; 56393f2aa4dSMichal Nazarewicz 56493f2aa4dSMichal Nazarewicz case PAT_PIPE: 56593f2aa4dSMichal Nazarewicz ret = fwrite(buf, nbytes, 1, stdout); 56693f2aa4dSMichal Nazarewicz if (ret > 0) 56793f2aa4dSMichal Nazarewicz fflush(stdout); 56893f2aa4dSMichal Nazarewicz break; 56993f2aa4dSMichal Nazarewicz 57093f2aa4dSMichal Nazarewicz invalid: 57193f2aa4dSMichal Nazarewicz err("bad OUT byte %zd, expected %02x got %02x\n", 57293f2aa4dSMichal Nazarewicz len, expected, *p); 57393f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) { 57493f2aa4dSMichal Nazarewicz if (0 == (len % 32)) 575d105e74eSDavidlohr Bueso fprintf(stderr, "%4zd:", len); 57693f2aa4dSMichal Nazarewicz fprintf(stderr, " %02x", *p); 57793f2aa4dSMichal Nazarewicz if (31 == (len % 32)) 57893f2aa4dSMichal Nazarewicz fprintf(stderr, "\n"); 57993f2aa4dSMichal Nazarewicz } 58093f2aa4dSMichal Nazarewicz fflush(stderr); 58193f2aa4dSMichal Nazarewicz errno = EILSEQ; 58293f2aa4dSMichal Nazarewicz return -1; 58393f2aa4dSMichal Nazarewicz } 58493f2aa4dSMichal Nazarewicz 58593f2aa4dSMichal Nazarewicz return len; 58693f2aa4dSMichal Nazarewicz } 58793f2aa4dSMichal Nazarewicz 58893f2aa4dSMichal Nazarewicz 58993f2aa4dSMichal Nazarewicz /******************** Endpoints routines ************************************/ 59093f2aa4dSMichal Nazarewicz 59193f2aa4dSMichal Nazarewicz static void handle_setup(const struct usb_ctrlrequest *setup) 59293f2aa4dSMichal Nazarewicz { 59393f2aa4dSMichal Nazarewicz printf("bRequestType = %d\n", setup->bRequestType); 59493f2aa4dSMichal Nazarewicz printf("bRequest = %d\n", setup->bRequest); 59593f2aa4dSMichal Nazarewicz printf("wValue = %d\n", le16_to_cpu(setup->wValue)); 59693f2aa4dSMichal Nazarewicz printf("wIndex = %d\n", le16_to_cpu(setup->wIndex)); 59793f2aa4dSMichal Nazarewicz printf("wLength = %d\n", le16_to_cpu(setup->wLength)); 59893f2aa4dSMichal Nazarewicz } 59993f2aa4dSMichal Nazarewicz 60093f2aa4dSMichal Nazarewicz static ssize_t 60193f2aa4dSMichal Nazarewicz ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) 60293f2aa4dSMichal Nazarewicz { 60393f2aa4dSMichal Nazarewicz static const char *const names[] = { 60493f2aa4dSMichal Nazarewicz [FUNCTIONFS_BIND] = "BIND", 60593f2aa4dSMichal Nazarewicz [FUNCTIONFS_UNBIND] = "UNBIND", 60693f2aa4dSMichal Nazarewicz [FUNCTIONFS_ENABLE] = "ENABLE", 60793f2aa4dSMichal Nazarewicz [FUNCTIONFS_DISABLE] = "DISABLE", 60893f2aa4dSMichal Nazarewicz [FUNCTIONFS_SETUP] = "SETUP", 60993f2aa4dSMichal Nazarewicz [FUNCTIONFS_SUSPEND] = "SUSPEND", 61093f2aa4dSMichal Nazarewicz [FUNCTIONFS_RESUME] = "RESUME", 61193f2aa4dSMichal Nazarewicz }; 61293f2aa4dSMichal Nazarewicz 61393f2aa4dSMichal Nazarewicz const struct usb_functionfs_event *event = buf; 61493f2aa4dSMichal Nazarewicz size_t n; 61593f2aa4dSMichal Nazarewicz 61693f2aa4dSMichal Nazarewicz (void)ignore; 61793f2aa4dSMichal Nazarewicz 61893f2aa4dSMichal Nazarewicz for (n = nbytes / sizeof *event; n; --n, ++event) 61993f2aa4dSMichal Nazarewicz switch (event->type) { 62093f2aa4dSMichal Nazarewicz case FUNCTIONFS_BIND: 62193f2aa4dSMichal Nazarewicz case FUNCTIONFS_UNBIND: 62293f2aa4dSMichal Nazarewicz case FUNCTIONFS_ENABLE: 62393f2aa4dSMichal Nazarewicz case FUNCTIONFS_DISABLE: 62493f2aa4dSMichal Nazarewicz case FUNCTIONFS_SETUP: 62593f2aa4dSMichal Nazarewicz case FUNCTIONFS_SUSPEND: 62693f2aa4dSMichal Nazarewicz case FUNCTIONFS_RESUME: 62793f2aa4dSMichal Nazarewicz printf("Event %s\n", names[event->type]); 62893f2aa4dSMichal Nazarewicz if (event->type == FUNCTIONFS_SETUP) 62993f2aa4dSMichal Nazarewicz handle_setup(&event->u.setup); 63093f2aa4dSMichal Nazarewicz break; 63193f2aa4dSMichal Nazarewicz 63293f2aa4dSMichal Nazarewicz default: 63393f2aa4dSMichal Nazarewicz printf("Event %03u (unknown)\n", event->type); 63493f2aa4dSMichal Nazarewicz } 63593f2aa4dSMichal Nazarewicz 63693f2aa4dSMichal Nazarewicz return nbytes; 63793f2aa4dSMichal Nazarewicz } 63893f2aa4dSMichal Nazarewicz 639b9a42746SMichal Nazarewicz static void ep0_init(struct thread *t, bool legacy_descriptors) 64093f2aa4dSMichal Nazarewicz { 641b9a42746SMichal Nazarewicz void *legacy; 64293f2aa4dSMichal Nazarewicz ssize_t ret; 643b9a42746SMichal Nazarewicz size_t len; 64493f2aa4dSMichal Nazarewicz 645b9a42746SMichal Nazarewicz if (legacy_descriptors) { 64693f2aa4dSMichal Nazarewicz info("%s: writing descriptors\n", t->filename); 647b9a42746SMichal Nazarewicz goto legacy; 648b9a42746SMichal Nazarewicz } 649b9a42746SMichal Nazarewicz 650b9a42746SMichal Nazarewicz info("%s: writing descriptors (in v2 format)\n", t->filename); 65193f2aa4dSMichal Nazarewicz ret = write(t->fd, &descriptors, sizeof descriptors); 652b9a42746SMichal Nazarewicz 653b9a42746SMichal Nazarewicz if (ret < 0 && errno == EINVAL) { 654b9a42746SMichal Nazarewicz warn("%s: new format rejected, trying legacy\n", t->filename); 655b9a42746SMichal Nazarewicz legacy: 656b9a42746SMichal Nazarewicz len = descs_to_legacy(&legacy, &descriptors); 657b9a42746SMichal Nazarewicz if (len) { 658b9a42746SMichal Nazarewicz ret = write(t->fd, legacy, len); 659b9a42746SMichal Nazarewicz free(legacy); 660b9a42746SMichal Nazarewicz } 661b9a42746SMichal Nazarewicz } 66293f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: descriptors", t->filename); 66393f2aa4dSMichal Nazarewicz 66493f2aa4dSMichal Nazarewicz info("%s: writing strings\n", t->filename); 66593f2aa4dSMichal Nazarewicz ret = write(t->fd, &strings, sizeof strings); 66693f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: strings", t->filename); 66793f2aa4dSMichal Nazarewicz } 66893f2aa4dSMichal Nazarewicz 66993f2aa4dSMichal Nazarewicz 67093f2aa4dSMichal Nazarewicz /******************** Main **************************************************/ 67193f2aa4dSMichal Nazarewicz 672b9a42746SMichal Nazarewicz int main(int argc, char **argv) 67393f2aa4dSMichal Nazarewicz { 674b9a42746SMichal Nazarewicz bool legacy_descriptors; 67593f2aa4dSMichal Nazarewicz unsigned i; 67693f2aa4dSMichal Nazarewicz 677b9a42746SMichal Nazarewicz legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l"); 67893f2aa4dSMichal Nazarewicz 67993f2aa4dSMichal Nazarewicz init_thread(threads); 680b9a42746SMichal Nazarewicz ep0_init(threads, legacy_descriptors); 68193f2aa4dSMichal Nazarewicz 68293f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 68393f2aa4dSMichal Nazarewicz init_thread(threads + i); 68493f2aa4dSMichal Nazarewicz 68593f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 68693f2aa4dSMichal Nazarewicz start_thread(threads + i); 68793f2aa4dSMichal Nazarewicz 68893f2aa4dSMichal Nazarewicz start_thread_helper(threads); 68993f2aa4dSMichal Nazarewicz 69093f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i) 69193f2aa4dSMichal Nazarewicz join_thread(threads + i); 69293f2aa4dSMichal Nazarewicz 69393f2aa4dSMichal Nazarewicz return 0; 69493f2aa4dSMichal Nazarewicz } 695