| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * ffs-test.c -- user mode filesystem api for usb composite function | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2010 Samsung Electronics | 
|  | 5 | *                    Author: Michal Nazarewicz <mina86@mina86.com> | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License as published by | 
|  | 9 | * the Free Software Foundation; either version 2 of the License, or | 
|  | 10 | * (at your option) any later version. | 
|  | 11 | * | 
|  | 12 | * This program is distributed in the hope that it will be useful, | 
|  | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | * GNU General Public License for more details. | 
|  | 16 | * | 
|  | 17 | * You should have received a copy of the GNU General Public License | 
|  | 18 | * along with this program; if not, write to the Free Software | 
|  | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | 20 | */ | 
|  | 21 |  | 
|  | 22 | /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */ | 
|  | 23 |  | 
|  | 24 |  | 
|  | 25 | #define _DEFAULT_SOURCE /* for endian.h */ | 
|  | 26 |  | 
|  | 27 | #include <endian.h> | 
|  | 28 | #include <errno.h> | 
|  | 29 | #include <fcntl.h> | 
|  | 30 | #include <pthread.h> | 
|  | 31 | #include <stdarg.h> | 
|  | 32 | #include <stdbool.h> | 
|  | 33 | #include <stdio.h> | 
|  | 34 | #include <stdlib.h> | 
|  | 35 | #include <string.h> | 
|  | 36 | #include <sys/ioctl.h> | 
|  | 37 | #include <sys/stat.h> | 
|  | 38 | #include <sys/types.h> | 
|  | 39 | #include <unistd.h> | 
|  | 40 | #include <tools/le_byteshift.h> | 
|  | 41 |  | 
|  | 42 | #include "../../include/uapi/linux/usb/functionfs.h" | 
|  | 43 |  | 
|  | 44 |  | 
|  | 45 | /******************** Little Endian Handling ********************************/ | 
|  | 46 |  | 
|  | 47 | /* | 
|  | 48 | * cpu_to_le16/32 are used when initializing structures, a context where a | 
|  | 49 | * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way | 
|  | 50 | * that allows them to be used when initializing structures. | 
|  | 51 | */ | 
|  | 52 |  | 
|  | 53 | #if __BYTE_ORDER == __LITTLE_ENDIAN | 
|  | 54 | #define cpu_to_le16(x)  (x) | 
|  | 55 | #define cpu_to_le32(x)  (x) | 
|  | 56 | #else | 
|  | 57 | #define cpu_to_le16(x)  ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) | 
|  | 58 | #define cpu_to_le32(x)  \ | 
|  | 59 | ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >>  8) | \ | 
|  | 60 | (((x) & 0x0000ff00u) <<  8) | (((x) & 0x000000ffu) << 24)) | 
|  | 61 | #endif | 
|  | 62 |  | 
|  | 63 | #define le32_to_cpu(x)  le32toh(x) | 
|  | 64 | #define le16_to_cpu(x)  le16toh(x) | 
|  | 65 |  | 
|  | 66 | /******************** Messages and Errors ***********************************/ | 
|  | 67 |  | 
|  | 68 | static const char argv0[] = "ffs-test"; | 
|  | 69 |  | 
|  | 70 | static unsigned verbosity = 7; | 
|  | 71 |  | 
|  | 72 | static void _msg(unsigned level, const char *fmt, ...) | 
|  | 73 | { | 
|  | 74 | if (level < 2) | 
|  | 75 | level = 2; | 
|  | 76 | else if (level > 7) | 
|  | 77 | level = 7; | 
|  | 78 |  | 
|  | 79 | if (level <= verbosity) { | 
|  | 80 | static const char levels[8][6] = { | 
|  | 81 | [2] = "crit:", | 
|  | 82 | [3] = "err: ", | 
|  | 83 | [4] = "warn:", | 
|  | 84 | [5] = "note:", | 
|  | 85 | [6] = "info:", | 
|  | 86 | [7] = "dbg: " | 
|  | 87 | }; | 
|  | 88 |  | 
|  | 89 | int _errno = errno; | 
|  | 90 | va_list ap; | 
|  | 91 |  | 
|  | 92 | fprintf(stderr, "%s: %s ", argv0, levels[level]); | 
|  | 93 | va_start(ap, fmt); | 
|  | 94 | vfprintf(stderr, fmt, ap); | 
|  | 95 | va_end(ap); | 
|  | 96 |  | 
|  | 97 | if (fmt[strlen(fmt) - 1] != '\n') { | 
|  | 98 | char buffer[128]; | 
|  | 99 | strerror_r(_errno, buffer, sizeof buffer); | 
|  | 100 | fprintf(stderr, ": (-%d) %s\n", _errno, buffer); | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | fflush(stderr); | 
|  | 104 | } | 
|  | 105 | } | 
|  | 106 |  | 
|  | 107 | #define die(...)  (_msg(2, __VA_ARGS__), exit(1)) | 
|  | 108 | #define err(...)   _msg(3, __VA_ARGS__) | 
|  | 109 | #define warn(...)  _msg(4, __VA_ARGS__) | 
|  | 110 | #define note(...)  _msg(5, __VA_ARGS__) | 
|  | 111 | #define info(...)  _msg(6, __VA_ARGS__) | 
|  | 112 | #define debug(...) _msg(7, __VA_ARGS__) | 
|  | 113 |  | 
|  | 114 | #define die_on(cond, ...) do { \ | 
|  | 115 | if (cond) \ | 
|  | 116 | die(__VA_ARGS__); \ | 
|  | 117 | } while (0) | 
|  | 118 |  | 
|  | 119 |  | 
|  | 120 | /******************** Descriptors and Strings *******************************/ | 
|  | 121 |  | 
|  | 122 | static const struct { | 
|  | 123 | struct usb_functionfs_descs_head_v2 header; | 
|  | 124 | __le32 fs_count; | 
|  | 125 | __le32 hs_count; | 
|  | 126 | __le32 ss_count; | 
|  | 127 | struct { | 
|  | 128 | struct usb_interface_descriptor intf; | 
|  | 129 | struct usb_endpoint_descriptor_no_audio sink; | 
|  | 130 | struct usb_endpoint_descriptor_no_audio source; | 
|  | 131 | } __attribute__((packed)) fs_descs, hs_descs; | 
|  | 132 | struct { | 
|  | 133 | struct usb_interface_descriptor intf; | 
|  | 134 | struct usb_endpoint_descriptor_no_audio sink; | 
|  | 135 | struct usb_ss_ep_comp_descriptor sink_comp; | 
|  | 136 | struct usb_endpoint_descriptor_no_audio source; | 
|  | 137 | struct usb_ss_ep_comp_descriptor source_comp; | 
|  | 138 | } ss_descs; | 
|  | 139 | } __attribute__((packed)) descriptors = { | 
|  | 140 | .header = { | 
|  | 141 | .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), | 
|  | 142 | .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | | 
|  | 143 | FUNCTIONFS_HAS_HS_DESC | | 
|  | 144 | FUNCTIONFS_HAS_SS_DESC), | 
|  | 145 | .length = cpu_to_le32(sizeof descriptors), | 
|  | 146 | }, | 
|  | 147 | .fs_count = cpu_to_le32(3), | 
|  | 148 | .fs_descs = { | 
|  | 149 | .intf = { | 
|  | 150 | .bLength = sizeof descriptors.fs_descs.intf, | 
|  | 151 | .bDescriptorType = USB_DT_INTERFACE, | 
|  | 152 | .bNumEndpoints = 2, | 
|  | 153 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | 
|  | 154 | .iInterface = 1, | 
|  | 155 | }, | 
|  | 156 | .sink = { | 
|  | 157 | .bLength = sizeof descriptors.fs_descs.sink, | 
|  | 158 | .bDescriptorType = USB_DT_ENDPOINT, | 
|  | 159 | .bEndpointAddress = 1 | USB_DIR_IN, | 
|  | 160 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | 
|  | 161 | /* .wMaxPacketSize = autoconfiguration (kernel) */ | 
|  | 162 | }, | 
|  | 163 | .source = { | 
|  | 164 | .bLength = sizeof descriptors.fs_descs.source, | 
|  | 165 | .bDescriptorType = USB_DT_ENDPOINT, | 
|  | 166 | .bEndpointAddress = 2 | USB_DIR_OUT, | 
|  | 167 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | 
|  | 168 | /* .wMaxPacketSize = autoconfiguration (kernel) */ | 
|  | 169 | }, | 
|  | 170 | }, | 
|  | 171 | .hs_count = cpu_to_le32(3), | 
|  | 172 | .hs_descs = { | 
|  | 173 | .intf = { | 
|  | 174 | .bLength = sizeof descriptors.fs_descs.intf, | 
|  | 175 | .bDescriptorType = USB_DT_INTERFACE, | 
|  | 176 | .bNumEndpoints = 2, | 
|  | 177 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | 
|  | 178 | .iInterface = 1, | 
|  | 179 | }, | 
|  | 180 | .sink = { | 
|  | 181 | .bLength = sizeof descriptors.hs_descs.sink, | 
|  | 182 | .bDescriptorType = USB_DT_ENDPOINT, | 
|  | 183 | .bEndpointAddress = 1 | USB_DIR_IN, | 
|  | 184 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | 
|  | 185 | .wMaxPacketSize = cpu_to_le16(512), | 
|  | 186 | }, | 
|  | 187 | .source = { | 
|  | 188 | .bLength = sizeof descriptors.hs_descs.source, | 
|  | 189 | .bDescriptorType = USB_DT_ENDPOINT, | 
|  | 190 | .bEndpointAddress = 2 | USB_DIR_OUT, | 
|  | 191 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | 
|  | 192 | .wMaxPacketSize = cpu_to_le16(512), | 
|  | 193 | .bInterval = 1, /* NAK every 1 uframe */ | 
|  | 194 | }, | 
|  | 195 | }, | 
|  | 196 | .ss_count = cpu_to_le32(5), | 
|  | 197 | .ss_descs = { | 
|  | 198 | .intf = { | 
|  | 199 | .bLength = sizeof descriptors.fs_descs.intf, | 
|  | 200 | .bDescriptorType = USB_DT_INTERFACE, | 
|  | 201 | .bNumEndpoints = 2, | 
|  | 202 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | 
|  | 203 | .iInterface = 1, | 
|  | 204 | }, | 
|  | 205 | .sink = { | 
|  | 206 | .bLength = sizeof descriptors.hs_descs.sink, | 
|  | 207 | .bDescriptorType = USB_DT_ENDPOINT, | 
|  | 208 | .bEndpointAddress = 1 | USB_DIR_IN, | 
|  | 209 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | 
|  | 210 | .wMaxPacketSize = cpu_to_le16(1024), | 
|  | 211 | }, | 
|  | 212 | .sink_comp = { | 
|  | 213 | .bLength = USB_DT_SS_EP_COMP_SIZE, | 
|  | 214 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | 
|  | 215 | .bMaxBurst = 0, | 
|  | 216 | .bmAttributes = 0, | 
|  | 217 | .wBytesPerInterval = 0, | 
|  | 218 | }, | 
|  | 219 | .source = { | 
|  | 220 | .bLength = sizeof descriptors.hs_descs.source, | 
|  | 221 | .bDescriptorType = USB_DT_ENDPOINT, | 
|  | 222 | .bEndpointAddress = 2 | USB_DIR_OUT, | 
|  | 223 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | 
|  | 224 | .wMaxPacketSize = cpu_to_le16(1024), | 
|  | 225 | .bInterval = 1, /* NAK every 1 uframe */ | 
|  | 226 | }, | 
|  | 227 | .source_comp = { | 
|  | 228 | .bLength = USB_DT_SS_EP_COMP_SIZE, | 
|  | 229 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | 
|  | 230 | .bMaxBurst = 0, | 
|  | 231 | .bmAttributes = 0, | 
|  | 232 | .wBytesPerInterval = 0, | 
|  | 233 | }, | 
|  | 234 | }, | 
|  | 235 | }; | 
|  | 236 |  | 
|  | 237 | static size_t descs_to_legacy(void **legacy, const void *descriptors_v2) | 
|  | 238 | { | 
|  | 239 | const unsigned char *descs_end, *descs_start; | 
|  | 240 | __u32 length, fs_count = 0, hs_count = 0, count; | 
|  | 241 |  | 
|  | 242 | /* Read v2 header */ | 
|  | 243 | { | 
|  | 244 | const struct { | 
|  | 245 | const struct usb_functionfs_descs_head_v2 header; | 
|  | 246 | const __le32 counts[]; | 
|  | 247 | } __attribute__((packed)) *const in = descriptors_v2; | 
|  | 248 | const __le32 *counts = in->counts; | 
|  | 249 | __u32 flags; | 
|  | 250 |  | 
|  | 251 | if (le32_to_cpu(in->header.magic) != | 
|  | 252 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2) | 
|  | 253 | return 0; | 
|  | 254 | length = le32_to_cpu(in->header.length); | 
|  | 255 | if (length <= sizeof in->header) | 
|  | 256 | return 0; | 
|  | 257 | length -= sizeof in->header; | 
|  | 258 | flags = le32_to_cpu(in->header.flags); | 
|  | 259 | if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | | 
|  | 260 | FUNCTIONFS_HAS_SS_DESC)) | 
|  | 261 | return 0; | 
|  | 262 |  | 
|  | 263 | #define GET_NEXT_COUNT_IF_FLAG(ret, flg) do {		\ | 
|  | 264 | if (!(flags & (flg)))		\ | 
|  | 265 | break;			\ | 
|  | 266 | if (length < 4)			\ | 
|  | 267 | return 0;		\ | 
|  | 268 | ret = le32_to_cpu(*counts);	\ | 
|  | 269 | length -= 4;			\ | 
|  | 270 | ++counts;			\ | 
|  | 271 | } while (0) | 
|  | 272 |  | 
|  | 273 | GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC); | 
|  | 274 | GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC); | 
|  | 275 | GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC); | 
|  | 276 |  | 
|  | 277 | count = fs_count + hs_count; | 
|  | 278 | if (!count) | 
|  | 279 | return 0; | 
|  | 280 | descs_start = (const void *)counts; | 
|  | 281 |  | 
|  | 282 | #undef GET_NEXT_COUNT_IF_FLAG | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | /* | 
|  | 286 | * Find the end of FS and HS USB descriptors.  SS descriptors | 
|  | 287 | * are ignored since legacy format does not support them. | 
|  | 288 | */ | 
|  | 289 | descs_end = descs_start; | 
|  | 290 | do { | 
|  | 291 | if (length < *descs_end) | 
|  | 292 | return 0; | 
|  | 293 | length -= *descs_end; | 
|  | 294 | descs_end += *descs_end; | 
|  | 295 | } while (--count); | 
|  | 296 |  | 
|  | 297 | /* Allocate legacy descriptors and copy the data. */ | 
|  | 298 | { | 
|  | 299 | #pragma GCC diagnostic push | 
|  | 300 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | 
|  | 301 | struct { | 
|  | 302 | struct usb_functionfs_descs_head header; | 
|  | 303 | __u8 descriptors[]; | 
|  | 304 | } __attribute__((packed)) *out; | 
|  | 305 | #pragma GCC diagnostic pop | 
|  | 306 |  | 
|  | 307 | length = sizeof out->header + (descs_end - descs_start); | 
|  | 308 | out = malloc(length); | 
|  | 309 | out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); | 
|  | 310 | out->header.length = cpu_to_le32(length); | 
|  | 311 | out->header.fs_count = cpu_to_le32(fs_count); | 
|  | 312 | out->header.hs_count = cpu_to_le32(hs_count); | 
|  | 313 | memcpy(out->descriptors, descs_start, descs_end - descs_start); | 
|  | 314 | *legacy = out; | 
|  | 315 | } | 
|  | 316 |  | 
|  | 317 | return length; | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 |  | 
|  | 321 | #define STR_INTERFACE_ "Source/Sink" | 
|  | 322 |  | 
|  | 323 | static const struct { | 
|  | 324 | struct usb_functionfs_strings_head header; | 
|  | 325 | struct { | 
|  | 326 | __le16 code; | 
|  | 327 | const char str1[sizeof STR_INTERFACE_]; | 
|  | 328 | } __attribute__((packed)) lang0; | 
|  | 329 | } __attribute__((packed)) strings = { | 
|  | 330 | .header = { | 
|  | 331 | .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), | 
|  | 332 | .length = cpu_to_le32(sizeof strings), | 
|  | 333 | .str_count = cpu_to_le32(1), | 
|  | 334 | .lang_count = cpu_to_le32(1), | 
|  | 335 | }, | 
|  | 336 | .lang0 = { | 
|  | 337 | cpu_to_le16(0x0409), /* en-us */ | 
|  | 338 | STR_INTERFACE_, | 
|  | 339 | }, | 
|  | 340 | }; | 
|  | 341 |  | 
|  | 342 | #define STR_INTERFACE strings.lang0.str1 | 
|  | 343 |  | 
|  | 344 |  | 
|  | 345 | /******************** Files and Threads Handling ****************************/ | 
|  | 346 |  | 
|  | 347 | struct thread; | 
|  | 348 |  | 
|  | 349 | static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes); | 
|  | 350 | static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes); | 
|  | 351 | static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes); | 
|  | 352 | static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes); | 
|  | 353 | static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes); | 
|  | 354 |  | 
|  | 355 |  | 
|  | 356 | static struct thread { | 
|  | 357 | const char *const filename; | 
|  | 358 | size_t buf_size; | 
|  | 359 |  | 
|  | 360 | ssize_t (*in)(struct thread *, void *, size_t); | 
|  | 361 | const char *const in_name; | 
|  | 362 |  | 
|  | 363 | ssize_t (*out)(struct thread *, const void *, size_t); | 
|  | 364 | const char *const out_name; | 
|  | 365 |  | 
|  | 366 | int fd; | 
|  | 367 | pthread_t id; | 
|  | 368 | void *buf; | 
|  | 369 | ssize_t status; | 
|  | 370 | } threads[] = { | 
|  | 371 | { | 
|  | 372 | "ep0", 4 * sizeof(struct usb_functionfs_event), | 
|  | 373 | read_wrap, NULL, | 
|  | 374 | ep0_consume, "<consume>", | 
|  | 375 | 0, 0, NULL, 0 | 
|  | 376 | }, | 
|  | 377 | { | 
|  | 378 | "ep1", 8 * 1024, | 
|  | 379 | fill_in_buf, "<in>", | 
|  | 380 | write_wrap, NULL, | 
|  | 381 | 0, 0, NULL, 0 | 
|  | 382 | }, | 
|  | 383 | { | 
|  | 384 | "ep2", 8 * 1024, | 
|  | 385 | read_wrap, NULL, | 
|  | 386 | empty_out_buf, "<out>", | 
|  | 387 | 0, 0, NULL, 0 | 
|  | 388 | }, | 
|  | 389 | }; | 
|  | 390 |  | 
|  | 391 |  | 
|  | 392 | static void init_thread(struct thread *t) | 
|  | 393 | { | 
|  | 394 | t->buf = malloc(t->buf_size); | 
|  | 395 | die_on(!t->buf, "malloc"); | 
|  | 396 |  | 
|  | 397 | t->fd = open(t->filename, O_RDWR); | 
|  | 398 | die_on(t->fd < 0, "%s", t->filename); | 
|  | 399 | } | 
|  | 400 |  | 
|  | 401 | static void cleanup_thread(void *arg) | 
|  | 402 | { | 
|  | 403 | struct thread *t = arg; | 
|  | 404 | int ret, fd; | 
|  | 405 |  | 
|  | 406 | fd = t->fd; | 
|  | 407 | if (t->fd < 0) | 
|  | 408 | return; | 
|  | 409 | t->fd = -1; | 
|  | 410 |  | 
|  | 411 | /* test the FIFO ioctls (non-ep0 code paths) */ | 
|  | 412 | if (t != threads) { | 
|  | 413 | ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS); | 
|  | 414 | if (ret < 0) { | 
|  | 415 | /* ENODEV reported after disconnect */ | 
|  | 416 | if (errno != ENODEV) | 
|  | 417 | err("%s: get fifo status", t->filename); | 
|  | 418 | } else if (ret) { | 
|  | 419 | warn("%s: unclaimed = %d\n", t->filename, ret); | 
|  | 420 | if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0) | 
|  | 421 | err("%s: fifo flush", t->filename); | 
|  | 422 | } | 
|  | 423 | } | 
|  | 424 |  | 
|  | 425 | if (close(fd) < 0) | 
|  | 426 | err("%s: close", t->filename); | 
|  | 427 |  | 
|  | 428 | free(t->buf); | 
|  | 429 | t->buf = NULL; | 
|  | 430 | } | 
|  | 431 |  | 
|  | 432 | static void *start_thread_helper(void *arg) | 
|  | 433 | { | 
|  | 434 | const char *name, *op, *in_name, *out_name; | 
|  | 435 | struct thread *t = arg; | 
|  | 436 | ssize_t ret; | 
|  | 437 |  | 
|  | 438 | info("%s: starts\n", t->filename); | 
|  | 439 | in_name = t->in_name ? t->in_name : t->filename; | 
|  | 440 | out_name = t->out_name ? t->out_name : t->filename; | 
|  | 441 |  | 
|  | 442 | pthread_cleanup_push(cleanup_thread, arg); | 
|  | 443 |  | 
|  | 444 | for (;;) { | 
|  | 445 | pthread_testcancel(); | 
|  | 446 |  | 
|  | 447 | ret = t->in(t, t->buf, t->buf_size); | 
|  | 448 | if (ret > 0) { | 
|  | 449 | ret = t->out(t, t->buf, ret); | 
|  | 450 | name = out_name; | 
|  | 451 | op = "write"; | 
|  | 452 | } else { | 
|  | 453 | name = in_name; | 
|  | 454 | op = "read"; | 
|  | 455 | } | 
|  | 456 |  | 
|  | 457 | if (ret > 0) { | 
|  | 458 | /* nop */ | 
|  | 459 | } else if (!ret) { | 
|  | 460 | debug("%s: %s: EOF", name, op); | 
|  | 461 | break; | 
|  | 462 | } else if (errno == EINTR || errno == EAGAIN) { | 
|  | 463 | debug("%s: %s", name, op); | 
|  | 464 | } else { | 
|  | 465 | warn("%s: %s", name, op); | 
|  | 466 | break; | 
|  | 467 | } | 
|  | 468 | } | 
|  | 469 |  | 
|  | 470 | pthread_cleanup_pop(1); | 
|  | 471 |  | 
|  | 472 | t->status = ret; | 
|  | 473 | info("%s: ends\n", t->filename); | 
|  | 474 | return NULL; | 
|  | 475 | } | 
|  | 476 |  | 
|  | 477 | static void start_thread(struct thread *t) | 
|  | 478 | { | 
|  | 479 | debug("%s: starting\n", t->filename); | 
|  | 480 |  | 
|  | 481 | die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0, | 
|  | 482 | "pthread_create(%s)", t->filename); | 
|  | 483 | } | 
|  | 484 |  | 
|  | 485 | static void join_thread(struct thread *t) | 
|  | 486 | { | 
|  | 487 | int ret = pthread_join(t->id, NULL); | 
|  | 488 |  | 
|  | 489 | if (ret < 0) | 
|  | 490 | err("%s: joining thread", t->filename); | 
|  | 491 | else | 
|  | 492 | debug("%s: joined\n", t->filename); | 
|  | 493 | } | 
|  | 494 |  | 
|  | 495 |  | 
|  | 496 | static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes) | 
|  | 497 | { | 
|  | 498 | return read(t->fd, buf, nbytes); | 
|  | 499 | } | 
|  | 500 |  | 
|  | 501 | static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes) | 
|  | 502 | { | 
|  | 503 | return write(t->fd, buf, nbytes); | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 |  | 
|  | 507 | /******************** Empty/Fill buffer routines ****************************/ | 
|  | 508 |  | 
|  | 509 | /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */ | 
|  | 510 | enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE }; | 
|  | 511 | static enum pattern pattern; | 
|  | 512 |  | 
|  | 513 | static ssize_t | 
|  | 514 | fill_in_buf(struct thread *ignore, void *buf, size_t nbytes) | 
|  | 515 | { | 
|  | 516 | size_t i; | 
|  | 517 | __u8 *p; | 
|  | 518 |  | 
|  | 519 | (void)ignore; | 
|  | 520 |  | 
|  | 521 | switch (pattern) { | 
|  | 522 | case PAT_ZERO: | 
|  | 523 | memset(buf, 0, nbytes); | 
|  | 524 | break; | 
|  | 525 |  | 
|  | 526 | case PAT_SEQ: | 
|  | 527 | for (p = buf, i = 0; i < nbytes; ++i, ++p) | 
|  | 528 | *p = i % 63; | 
|  | 529 | break; | 
|  | 530 |  | 
|  | 531 | case PAT_PIPE: | 
|  | 532 | return fread(buf, 1, nbytes, stdin); | 
|  | 533 | } | 
|  | 534 |  | 
|  | 535 | return nbytes; | 
|  | 536 | } | 
|  | 537 |  | 
|  | 538 | static ssize_t | 
|  | 539 | empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes) | 
|  | 540 | { | 
|  | 541 | const __u8 *p; | 
|  | 542 | __u8 expected; | 
|  | 543 | ssize_t ret; | 
|  | 544 | size_t len; | 
|  | 545 |  | 
|  | 546 | (void)ignore; | 
|  | 547 |  | 
|  | 548 | switch (pattern) { | 
|  | 549 | case PAT_ZERO: | 
|  | 550 | expected = 0; | 
|  | 551 | for (p = buf, len = 0; len < nbytes; ++p, ++len) | 
|  | 552 | if (*p) | 
|  | 553 | goto invalid; | 
|  | 554 | break; | 
|  | 555 |  | 
|  | 556 | case PAT_SEQ: | 
|  | 557 | for (p = buf, len = 0; len < nbytes; ++p, ++len) | 
|  | 558 | if (*p != len % 63) { | 
|  | 559 | expected = len % 63; | 
|  | 560 | goto invalid; | 
|  | 561 | } | 
|  | 562 | break; | 
|  | 563 |  | 
|  | 564 | case PAT_PIPE: | 
|  | 565 | ret = fwrite(buf, nbytes, 1, stdout); | 
|  | 566 | if (ret > 0) | 
|  | 567 | fflush(stdout); | 
|  | 568 | break; | 
|  | 569 |  | 
|  | 570 | invalid: | 
|  | 571 | err("bad OUT byte %zd, expected %02x got %02x\n", | 
|  | 572 | len, expected, *p); | 
|  | 573 | for (p = buf, len = 0; len < nbytes; ++p, ++len) { | 
|  | 574 | if (0 == (len % 32)) | 
|  | 575 | fprintf(stderr, "%4zd:", len); | 
|  | 576 | fprintf(stderr, " %02x", *p); | 
|  | 577 | if (31 == (len % 32)) | 
|  | 578 | fprintf(stderr, "\n"); | 
|  | 579 | } | 
|  | 580 | fflush(stderr); | 
|  | 581 | errno = EILSEQ; | 
|  | 582 | return -1; | 
|  | 583 | } | 
|  | 584 |  | 
|  | 585 | return len; | 
|  | 586 | } | 
|  | 587 |  | 
|  | 588 |  | 
|  | 589 | /******************** Endpoints routines ************************************/ | 
|  | 590 |  | 
|  | 591 | static void handle_setup(const struct usb_ctrlrequest *setup) | 
|  | 592 | { | 
|  | 593 | printf("bRequestType = %d\n", setup->bRequestType); | 
|  | 594 | printf("bRequest     = %d\n", setup->bRequest); | 
|  | 595 | printf("wValue       = %d\n", le16_to_cpu(setup->wValue)); | 
|  | 596 | printf("wIndex       = %d\n", le16_to_cpu(setup->wIndex)); | 
|  | 597 | printf("wLength      = %d\n", le16_to_cpu(setup->wLength)); | 
|  | 598 | } | 
|  | 599 |  | 
|  | 600 | static ssize_t | 
|  | 601 | ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) | 
|  | 602 | { | 
|  | 603 | static const char *const names[] = { | 
|  | 604 | [FUNCTIONFS_BIND] = "BIND", | 
|  | 605 | [FUNCTIONFS_UNBIND] = "UNBIND", | 
|  | 606 | [FUNCTIONFS_ENABLE] = "ENABLE", | 
|  | 607 | [FUNCTIONFS_DISABLE] = "DISABLE", | 
|  | 608 | [FUNCTIONFS_SETUP] = "SETUP", | 
|  | 609 | [FUNCTIONFS_SUSPEND] = "SUSPEND", | 
|  | 610 | [FUNCTIONFS_RESUME] = "RESUME", | 
|  | 611 | }; | 
|  | 612 |  | 
|  | 613 | const struct usb_functionfs_event *event = buf; | 
|  | 614 | size_t n; | 
|  | 615 |  | 
|  | 616 | (void)ignore; | 
|  | 617 |  | 
|  | 618 | for (n = nbytes / sizeof *event; n; --n, ++event) | 
|  | 619 | switch (event->type) { | 
|  | 620 | case FUNCTIONFS_BIND: | 
|  | 621 | case FUNCTIONFS_UNBIND: | 
|  | 622 | case FUNCTIONFS_ENABLE: | 
|  | 623 | case FUNCTIONFS_DISABLE: | 
|  | 624 | case FUNCTIONFS_SETUP: | 
|  | 625 | case FUNCTIONFS_SUSPEND: | 
|  | 626 | case FUNCTIONFS_RESUME: | 
|  | 627 | printf("Event %s\n", names[event->type]); | 
|  | 628 | if (event->type == FUNCTIONFS_SETUP) | 
|  | 629 | handle_setup(&event->u.setup); | 
|  | 630 | break; | 
|  | 631 |  | 
|  | 632 | default: | 
|  | 633 | printf("Event %03u (unknown)\n", event->type); | 
|  | 634 | } | 
|  | 635 |  | 
|  | 636 | return nbytes; | 
|  | 637 | } | 
|  | 638 |  | 
|  | 639 | static void ep0_init(struct thread *t, bool legacy_descriptors) | 
|  | 640 | { | 
|  | 641 | void *legacy; | 
|  | 642 | ssize_t ret; | 
|  | 643 | size_t len; | 
|  | 644 |  | 
|  | 645 | if (legacy_descriptors) { | 
|  | 646 | info("%s: writing descriptors\n", t->filename); | 
|  | 647 | goto legacy; | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 | info("%s: writing descriptors (in v2 format)\n", t->filename); | 
|  | 651 | ret = write(t->fd, &descriptors, sizeof descriptors); | 
|  | 652 |  | 
|  | 653 | if (ret < 0 && errno == EINVAL) { | 
|  | 654 | warn("%s: new format rejected, trying legacy\n", t->filename); | 
|  | 655 | legacy: | 
|  | 656 | len = descs_to_legacy(&legacy, &descriptors); | 
|  | 657 | if (len) { | 
|  | 658 | ret = write(t->fd, legacy, len); | 
|  | 659 | free(legacy); | 
|  | 660 | } | 
|  | 661 | } | 
|  | 662 | die_on(ret < 0, "%s: write: descriptors", t->filename); | 
|  | 663 |  | 
|  | 664 | info("%s: writing strings\n", t->filename); | 
|  | 665 | ret = write(t->fd, &strings, sizeof strings); | 
|  | 666 | die_on(ret < 0, "%s: write: strings", t->filename); | 
|  | 667 | } | 
|  | 668 |  | 
|  | 669 |  | 
|  | 670 | /******************** Main **************************************************/ | 
|  | 671 |  | 
|  | 672 | int main(int argc, char **argv) | 
|  | 673 | { | 
|  | 674 | bool legacy_descriptors; | 
|  | 675 | unsigned i; | 
|  | 676 |  | 
|  | 677 | legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l"); | 
|  | 678 |  | 
|  | 679 | init_thread(threads); | 
|  | 680 | ep0_init(threads, legacy_descriptors); | 
|  | 681 |  | 
|  | 682 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) | 
|  | 683 | init_thread(threads + i); | 
|  | 684 |  | 
|  | 685 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) | 
|  | 686 | start_thread(threads + i); | 
|  | 687 |  | 
|  | 688 | start_thread_helper(threads); | 
|  | 689 |  | 
|  | 690 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) | 
|  | 691 | join_thread(threads + i); | 
|  | 692 |  | 
|  | 693 | return 0; | 
|  | 694 | } |