1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
293f2aa4dSMichal Nazarewicz /*
351c208c7SMichal Nazarewicz * ffs-test.c -- user mode filesystem api for usb composite function
493f2aa4dSMichal Nazarewicz *
593f2aa4dSMichal Nazarewicz * Copyright (C) 2010 Samsung Electronics
654b8360fSMichal Nazarewicz * Author: Michal Nazarewicz <mina86@mina86.com>
793f2aa4dSMichal Nazarewicz */
893f2aa4dSMichal Nazarewicz
993f2aa4dSMichal Nazarewicz /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
1093f2aa4dSMichal Nazarewicz
1193f2aa4dSMichal Nazarewicz
125abb9b91SFelipe Balbi #define _DEFAULT_SOURCE /* for endian.h */
1393f2aa4dSMichal Nazarewicz
1493f2aa4dSMichal Nazarewicz #include <endian.h>
1593f2aa4dSMichal Nazarewicz #include <errno.h>
1693f2aa4dSMichal Nazarewicz #include <fcntl.h>
1793f2aa4dSMichal Nazarewicz #include <pthread.h>
1893f2aa4dSMichal Nazarewicz #include <stdarg.h>
19b9a42746SMichal Nazarewicz #include <stdbool.h>
2093f2aa4dSMichal Nazarewicz #include <stdio.h>
2193f2aa4dSMichal Nazarewicz #include <stdlib.h>
2293f2aa4dSMichal Nazarewicz #include <string.h>
2393f2aa4dSMichal Nazarewicz #include <sys/ioctl.h>
2493f2aa4dSMichal Nazarewicz #include <sys/stat.h>
2593f2aa4dSMichal Nazarewicz #include <sys/types.h>
2693f2aa4dSMichal Nazarewicz #include <unistd.h>
2724fa9a9dSMatt Fleming #include <tools/le_byteshift.h>
2893f2aa4dSMichal Nazarewicz
29a0f11aceSMaxin B. John #include "../../include/uapi/linux/usb/functionfs.h"
3093f2aa4dSMichal Nazarewicz
3193f2aa4dSMichal Nazarewicz
3293f2aa4dSMichal Nazarewicz /******************** Little Endian Handling ********************************/
3393f2aa4dSMichal Nazarewicz
34a2b22dddSPeter Senna Tschudin /*
35a2b22dddSPeter Senna Tschudin * cpu_to_le16/32 are used when initializing structures, a context where a
36a2b22dddSPeter Senna Tschudin * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way
37a2b22dddSPeter Senna Tschudin * that allows them to be used when initializing structures.
38a2b22dddSPeter Senna Tschudin */
39a2b22dddSPeter Senna Tschudin
40a2b22dddSPeter Senna Tschudin #if __BYTE_ORDER == __LITTLE_ENDIAN
41a2b22dddSPeter Senna Tschudin #define cpu_to_le16(x) (x)
42a2b22dddSPeter Senna Tschudin #define cpu_to_le32(x) (x)
43a2b22dddSPeter Senna Tschudin #else
44a2b22dddSPeter Senna Tschudin #define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
45a2b22dddSPeter Senna Tschudin #define cpu_to_le32(x) \
46a2b22dddSPeter Senna Tschudin ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
47a2b22dddSPeter Senna Tschudin (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
48a2b22dddSPeter Senna Tschudin #endif
49a2b22dddSPeter Senna Tschudin
5093f2aa4dSMichal Nazarewicz #define le32_to_cpu(x) le32toh(x)
5193f2aa4dSMichal Nazarewicz #define le16_to_cpu(x) le16toh(x)
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
_msg(unsigned level,const char * fmt,...)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;
113271d2d6dSFelipe Balbi __le32 ss_count;
11493f2aa4dSMichal Nazarewicz struct {
11593f2aa4dSMichal Nazarewicz struct usb_interface_descriptor intf;
11693f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio sink;
11793f2aa4dSMichal Nazarewicz struct usb_endpoint_descriptor_no_audio source;
11893f2aa4dSMichal Nazarewicz } __attribute__((packed)) fs_descs, hs_descs;
119271d2d6dSFelipe Balbi struct {
120271d2d6dSFelipe Balbi struct usb_interface_descriptor intf;
121271d2d6dSFelipe Balbi struct usb_endpoint_descriptor_no_audio sink;
122271d2d6dSFelipe Balbi struct usb_ss_ep_comp_descriptor sink_comp;
123271d2d6dSFelipe Balbi struct usb_endpoint_descriptor_no_audio source;
124271d2d6dSFelipe Balbi struct usb_ss_ep_comp_descriptor source_comp;
125271d2d6dSFelipe Balbi } ss_descs;
12693f2aa4dSMichal Nazarewicz } __attribute__((packed)) descriptors = {
12793f2aa4dSMichal Nazarewicz .header = {
12851c208c7SMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
12951c208c7SMichal Nazarewicz .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
130271d2d6dSFelipe Balbi FUNCTIONFS_HAS_HS_DESC |
131271d2d6dSFelipe Balbi FUNCTIONFS_HAS_SS_DESC),
13293f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof descriptors),
13393f2aa4dSMichal Nazarewicz },
13451c208c7SMichal Nazarewicz .fs_count = cpu_to_le32(3),
13593f2aa4dSMichal Nazarewicz .fs_descs = {
13693f2aa4dSMichal Nazarewicz .intf = {
13793f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf,
13893f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE,
13993f2aa4dSMichal Nazarewicz .bNumEndpoints = 2,
14093f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
14193f2aa4dSMichal Nazarewicz .iInterface = 1,
14293f2aa4dSMichal Nazarewicz },
14393f2aa4dSMichal Nazarewicz .sink = {
14493f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.sink,
14593f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT,
14693f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN,
14793f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
14893f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */
14993f2aa4dSMichal Nazarewicz },
15093f2aa4dSMichal Nazarewicz .source = {
15193f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.source,
15293f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT,
15393f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT,
15493f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
15593f2aa4dSMichal Nazarewicz /* .wMaxPacketSize = autoconfiguration (kernel) */
15693f2aa4dSMichal Nazarewicz },
15793f2aa4dSMichal Nazarewicz },
15851c208c7SMichal Nazarewicz .hs_count = cpu_to_le32(3),
15993f2aa4dSMichal Nazarewicz .hs_descs = {
16093f2aa4dSMichal Nazarewicz .intf = {
16193f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.fs_descs.intf,
16293f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_INTERFACE,
16393f2aa4dSMichal Nazarewicz .bNumEndpoints = 2,
16493f2aa4dSMichal Nazarewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
16593f2aa4dSMichal Nazarewicz .iInterface = 1,
16693f2aa4dSMichal Nazarewicz },
16793f2aa4dSMichal Nazarewicz .sink = {
16893f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.sink,
16993f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT,
17093f2aa4dSMichal Nazarewicz .bEndpointAddress = 1 | USB_DIR_IN,
17193f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
17293f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512),
17393f2aa4dSMichal Nazarewicz },
17493f2aa4dSMichal Nazarewicz .source = {
17593f2aa4dSMichal Nazarewicz .bLength = sizeof descriptors.hs_descs.source,
17693f2aa4dSMichal Nazarewicz .bDescriptorType = USB_DT_ENDPOINT,
17793f2aa4dSMichal Nazarewicz .bEndpointAddress = 2 | USB_DIR_OUT,
17893f2aa4dSMichal Nazarewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
17993f2aa4dSMichal Nazarewicz .wMaxPacketSize = cpu_to_le16(512),
18093f2aa4dSMichal Nazarewicz .bInterval = 1, /* NAK every 1 uframe */
18193f2aa4dSMichal Nazarewicz },
18293f2aa4dSMichal Nazarewicz },
183271d2d6dSFelipe Balbi .ss_count = cpu_to_le32(5),
184271d2d6dSFelipe Balbi .ss_descs = {
185271d2d6dSFelipe Balbi .intf = {
186271d2d6dSFelipe Balbi .bLength = sizeof descriptors.fs_descs.intf,
187271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_INTERFACE,
188271d2d6dSFelipe Balbi .bNumEndpoints = 2,
189271d2d6dSFelipe Balbi .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
190271d2d6dSFelipe Balbi .iInterface = 1,
191271d2d6dSFelipe Balbi },
192271d2d6dSFelipe Balbi .sink = {
193271d2d6dSFelipe Balbi .bLength = sizeof descriptors.hs_descs.sink,
194271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT,
195271d2d6dSFelipe Balbi .bEndpointAddress = 1 | USB_DIR_IN,
196271d2d6dSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK,
197271d2d6dSFelipe Balbi .wMaxPacketSize = cpu_to_le16(1024),
198271d2d6dSFelipe Balbi },
199271d2d6dSFelipe Balbi .sink_comp = {
200271d2d6dSFelipe Balbi .bLength = USB_DT_SS_EP_COMP_SIZE,
201271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
202271d2d6dSFelipe Balbi .bMaxBurst = 0,
203271d2d6dSFelipe Balbi .bmAttributes = 0,
204271d2d6dSFelipe Balbi .wBytesPerInterval = 0,
205271d2d6dSFelipe Balbi },
206271d2d6dSFelipe Balbi .source = {
207271d2d6dSFelipe Balbi .bLength = sizeof descriptors.hs_descs.source,
208271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT,
209271d2d6dSFelipe Balbi .bEndpointAddress = 2 | USB_DIR_OUT,
210271d2d6dSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK,
211271d2d6dSFelipe Balbi .wMaxPacketSize = cpu_to_le16(1024),
212271d2d6dSFelipe Balbi .bInterval = 1, /* NAK every 1 uframe */
213271d2d6dSFelipe Balbi },
214271d2d6dSFelipe Balbi .source_comp = {
215271d2d6dSFelipe Balbi .bLength = USB_DT_SS_EP_COMP_SIZE,
216271d2d6dSFelipe Balbi .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
217271d2d6dSFelipe Balbi .bMaxBurst = 0,
218271d2d6dSFelipe Balbi .bmAttributes = 0,
219271d2d6dSFelipe Balbi .wBytesPerInterval = 0,
220271d2d6dSFelipe Balbi },
221271d2d6dSFelipe Balbi },
22293f2aa4dSMichal Nazarewicz };
22393f2aa4dSMichal Nazarewicz
descs_to_legacy(void ** legacy,const void * descriptors_v2)224b9a42746SMichal Nazarewicz static size_t descs_to_legacy(void **legacy, const void *descriptors_v2)
225b9a42746SMichal Nazarewicz {
226b9a42746SMichal Nazarewicz const unsigned char *descs_end, *descs_start;
227b9a42746SMichal Nazarewicz __u32 length, fs_count = 0, hs_count = 0, count;
228b9a42746SMichal Nazarewicz
229b9a42746SMichal Nazarewicz /* Read v2 header */
230b9a42746SMichal Nazarewicz {
231b9a42746SMichal Nazarewicz const struct {
232b9a42746SMichal Nazarewicz const struct usb_functionfs_descs_head_v2 header;
233b9a42746SMichal Nazarewicz const __le32 counts[];
234b9a42746SMichal Nazarewicz } __attribute__((packed)) *const in = descriptors_v2;
235b9a42746SMichal Nazarewicz const __le32 *counts = in->counts;
236b9a42746SMichal Nazarewicz __u32 flags;
237b9a42746SMichal Nazarewicz
238b9a42746SMichal Nazarewicz if (le32_to_cpu(in->header.magic) !=
239b9a42746SMichal Nazarewicz FUNCTIONFS_DESCRIPTORS_MAGIC_V2)
240b9a42746SMichal Nazarewicz return 0;
241b9a42746SMichal Nazarewicz length = le32_to_cpu(in->header.length);
242b9a42746SMichal Nazarewicz if (length <= sizeof in->header)
243b9a42746SMichal Nazarewicz return 0;
244b9a42746SMichal Nazarewicz length -= sizeof in->header;
245b9a42746SMichal Nazarewicz flags = le32_to_cpu(in->header.flags);
246b9a42746SMichal Nazarewicz if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
247b9a42746SMichal Nazarewicz FUNCTIONFS_HAS_SS_DESC))
248b9a42746SMichal Nazarewicz return 0;
249b9a42746SMichal Nazarewicz
250b9a42746SMichal Nazarewicz #define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \
251b9a42746SMichal Nazarewicz if (!(flags & (flg))) \
252b9a42746SMichal Nazarewicz break; \
253b9a42746SMichal Nazarewicz if (length < 4) \
254b9a42746SMichal Nazarewicz return 0; \
255b9a42746SMichal Nazarewicz ret = le32_to_cpu(*counts); \
256b9a42746SMichal Nazarewicz length -= 4; \
257b9a42746SMichal Nazarewicz ++counts; \
258b9a42746SMichal Nazarewicz } while (0)
259b9a42746SMichal Nazarewicz
260b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC);
261b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC);
262b9a42746SMichal Nazarewicz GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC);
263b9a42746SMichal Nazarewicz
264b9a42746SMichal Nazarewicz count = fs_count + hs_count;
265b9a42746SMichal Nazarewicz if (!count)
266b9a42746SMichal Nazarewicz return 0;
267b9a42746SMichal Nazarewicz descs_start = (const void *)counts;
268b9a42746SMichal Nazarewicz
269b9a42746SMichal Nazarewicz #undef GET_NEXT_COUNT_IF_FLAG
270b9a42746SMichal Nazarewicz }
271b9a42746SMichal Nazarewicz
272b9a42746SMichal Nazarewicz /*
273b9a42746SMichal Nazarewicz * Find the end of FS and HS USB descriptors. SS descriptors
274b9a42746SMichal Nazarewicz * are ignored since legacy format does not support them.
275b9a42746SMichal Nazarewicz */
276b9a42746SMichal Nazarewicz descs_end = descs_start;
277b9a42746SMichal Nazarewicz do {
278b9a42746SMichal Nazarewicz if (length < *descs_end)
279b9a42746SMichal Nazarewicz return 0;
280b9a42746SMichal Nazarewicz length -= *descs_end;
281b9a42746SMichal Nazarewicz descs_end += *descs_end;
282b9a42746SMichal Nazarewicz } while (--count);
283b9a42746SMichal Nazarewicz
284b9a42746SMichal Nazarewicz /* Allocate legacy descriptors and copy the data. */
285b9a42746SMichal Nazarewicz {
286b9a42746SMichal Nazarewicz #pragma GCC diagnostic push
287b9a42746SMichal Nazarewicz #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
288b9a42746SMichal Nazarewicz struct {
289b9a42746SMichal Nazarewicz struct usb_functionfs_descs_head header;
290b9a42746SMichal Nazarewicz __u8 descriptors[];
291b9a42746SMichal Nazarewicz } __attribute__((packed)) *out;
292b9a42746SMichal Nazarewicz #pragma GCC diagnostic pop
293b9a42746SMichal Nazarewicz
294b9a42746SMichal Nazarewicz length = sizeof out->header + (descs_end - descs_start);
295b9a42746SMichal Nazarewicz out = malloc(length);
296b9a42746SMichal Nazarewicz out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
297b9a42746SMichal Nazarewicz out->header.length = cpu_to_le32(length);
298b9a42746SMichal Nazarewicz out->header.fs_count = cpu_to_le32(fs_count);
299b9a42746SMichal Nazarewicz out->header.hs_count = cpu_to_le32(hs_count);
300b9a42746SMichal Nazarewicz memcpy(out->descriptors, descs_start, descs_end - descs_start);
301b9a42746SMichal Nazarewicz *legacy = out;
302b9a42746SMichal Nazarewicz }
303b9a42746SMichal Nazarewicz
304b9a42746SMichal Nazarewicz return length;
305b9a42746SMichal Nazarewicz }
306b9a42746SMichal Nazarewicz
30793f2aa4dSMichal Nazarewicz
30893f2aa4dSMichal Nazarewicz #define STR_INTERFACE_ "Source/Sink"
30993f2aa4dSMichal Nazarewicz
31093f2aa4dSMichal Nazarewicz static const struct {
31193f2aa4dSMichal Nazarewicz struct usb_functionfs_strings_head header;
31293f2aa4dSMichal Nazarewicz struct {
31393f2aa4dSMichal Nazarewicz __le16 code;
31493f2aa4dSMichal Nazarewicz const char str1[sizeof STR_INTERFACE_];
31593f2aa4dSMichal Nazarewicz } __attribute__((packed)) lang0;
31693f2aa4dSMichal Nazarewicz } __attribute__((packed)) strings = {
31793f2aa4dSMichal Nazarewicz .header = {
31893f2aa4dSMichal Nazarewicz .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
31993f2aa4dSMichal Nazarewicz .length = cpu_to_le32(sizeof strings),
32093f2aa4dSMichal Nazarewicz .str_count = cpu_to_le32(1),
32193f2aa4dSMichal Nazarewicz .lang_count = cpu_to_le32(1),
32293f2aa4dSMichal Nazarewicz },
32393f2aa4dSMichal Nazarewicz .lang0 = {
32493f2aa4dSMichal Nazarewicz cpu_to_le16(0x0409), /* en-us */
32593f2aa4dSMichal Nazarewicz STR_INTERFACE_,
32693f2aa4dSMichal Nazarewicz },
32793f2aa4dSMichal Nazarewicz };
32893f2aa4dSMichal Nazarewicz
32993f2aa4dSMichal Nazarewicz #define STR_INTERFACE strings.lang0.str1
33093f2aa4dSMichal Nazarewicz
33193f2aa4dSMichal Nazarewicz
33293f2aa4dSMichal Nazarewicz /******************** Files and Threads Handling ****************************/
33393f2aa4dSMichal Nazarewicz
33493f2aa4dSMichal Nazarewicz struct thread;
33593f2aa4dSMichal Nazarewicz
33693f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
33793f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
33893f2aa4dSMichal Nazarewicz static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
33993f2aa4dSMichal Nazarewicz static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
34093f2aa4dSMichal Nazarewicz static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
34193f2aa4dSMichal Nazarewicz
34293f2aa4dSMichal Nazarewicz
34393f2aa4dSMichal Nazarewicz static struct thread {
34493f2aa4dSMichal Nazarewicz const char *const filename;
34593f2aa4dSMichal Nazarewicz size_t buf_size;
34693f2aa4dSMichal Nazarewicz
34793f2aa4dSMichal Nazarewicz ssize_t (*in)(struct thread *, void *, size_t);
34893f2aa4dSMichal Nazarewicz const char *const in_name;
34993f2aa4dSMichal Nazarewicz
35093f2aa4dSMichal Nazarewicz ssize_t (*out)(struct thread *, const void *, size_t);
35193f2aa4dSMichal Nazarewicz const char *const out_name;
35293f2aa4dSMichal Nazarewicz
35393f2aa4dSMichal Nazarewicz int fd;
35493f2aa4dSMichal Nazarewicz pthread_t id;
35593f2aa4dSMichal Nazarewicz void *buf;
35693f2aa4dSMichal Nazarewicz ssize_t status;
35793f2aa4dSMichal Nazarewicz } threads[] = {
35893f2aa4dSMichal Nazarewicz {
35993f2aa4dSMichal Nazarewicz "ep0", 4 * sizeof(struct usb_functionfs_event),
36093f2aa4dSMichal Nazarewicz read_wrap, NULL,
36193f2aa4dSMichal Nazarewicz ep0_consume, "<consume>",
36293f2aa4dSMichal Nazarewicz 0, 0, NULL, 0
36393f2aa4dSMichal Nazarewicz },
36493f2aa4dSMichal Nazarewicz {
36593f2aa4dSMichal Nazarewicz "ep1", 8 * 1024,
36693f2aa4dSMichal Nazarewicz fill_in_buf, "<in>",
36793f2aa4dSMichal Nazarewicz write_wrap, NULL,
36893f2aa4dSMichal Nazarewicz 0, 0, NULL, 0
36993f2aa4dSMichal Nazarewicz },
37093f2aa4dSMichal Nazarewicz {
37193f2aa4dSMichal Nazarewicz "ep2", 8 * 1024,
37293f2aa4dSMichal Nazarewicz read_wrap, NULL,
37393f2aa4dSMichal Nazarewicz empty_out_buf, "<out>",
37493f2aa4dSMichal Nazarewicz 0, 0, NULL, 0
37593f2aa4dSMichal Nazarewicz },
37693f2aa4dSMichal Nazarewicz };
37793f2aa4dSMichal Nazarewicz
37893f2aa4dSMichal Nazarewicz
init_thread(struct thread * t)37993f2aa4dSMichal Nazarewicz static void init_thread(struct thread *t)
38093f2aa4dSMichal Nazarewicz {
38193f2aa4dSMichal Nazarewicz t->buf = malloc(t->buf_size);
38293f2aa4dSMichal Nazarewicz die_on(!t->buf, "malloc");
38393f2aa4dSMichal Nazarewicz
38493f2aa4dSMichal Nazarewicz t->fd = open(t->filename, O_RDWR);
38593f2aa4dSMichal Nazarewicz die_on(t->fd < 0, "%s", t->filename);
38693f2aa4dSMichal Nazarewicz }
38793f2aa4dSMichal Nazarewicz
cleanup_thread(void * arg)38893f2aa4dSMichal Nazarewicz static void cleanup_thread(void *arg)
38993f2aa4dSMichal Nazarewicz {
39093f2aa4dSMichal Nazarewicz struct thread *t = arg;
39193f2aa4dSMichal Nazarewicz int ret, fd;
39293f2aa4dSMichal Nazarewicz
39393f2aa4dSMichal Nazarewicz fd = t->fd;
39493f2aa4dSMichal Nazarewicz if (t->fd < 0)
39593f2aa4dSMichal Nazarewicz return;
39693f2aa4dSMichal Nazarewicz t->fd = -1;
39793f2aa4dSMichal Nazarewicz
39893f2aa4dSMichal Nazarewicz /* test the FIFO ioctls (non-ep0 code paths) */
39993f2aa4dSMichal Nazarewicz if (t != threads) {
40093f2aa4dSMichal Nazarewicz ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS);
40193f2aa4dSMichal Nazarewicz if (ret < 0) {
40293f2aa4dSMichal Nazarewicz /* ENODEV reported after disconnect */
40393f2aa4dSMichal Nazarewicz if (errno != ENODEV)
40493f2aa4dSMichal Nazarewicz err("%s: get fifo status", t->filename);
40593f2aa4dSMichal Nazarewicz } else if (ret) {
40693f2aa4dSMichal Nazarewicz warn("%s: unclaimed = %d\n", t->filename, ret);
40793f2aa4dSMichal Nazarewicz if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0)
40893f2aa4dSMichal Nazarewicz err("%s: fifo flush", t->filename);
40993f2aa4dSMichal Nazarewicz }
41093f2aa4dSMichal Nazarewicz }
41193f2aa4dSMichal Nazarewicz
41293f2aa4dSMichal Nazarewicz if (close(fd) < 0)
41393f2aa4dSMichal Nazarewicz err("%s: close", t->filename);
41493f2aa4dSMichal Nazarewicz
41593f2aa4dSMichal Nazarewicz free(t->buf);
41693f2aa4dSMichal Nazarewicz t->buf = NULL;
41793f2aa4dSMichal Nazarewicz }
41893f2aa4dSMichal Nazarewicz
start_thread_helper(void * arg)41993f2aa4dSMichal Nazarewicz static void *start_thread_helper(void *arg)
42093f2aa4dSMichal Nazarewicz {
42193f2aa4dSMichal Nazarewicz const char *name, *op, *in_name, *out_name;
42293f2aa4dSMichal Nazarewicz struct thread *t = arg;
42393f2aa4dSMichal Nazarewicz ssize_t ret;
42493f2aa4dSMichal Nazarewicz
42593f2aa4dSMichal Nazarewicz info("%s: starts\n", t->filename);
42693f2aa4dSMichal Nazarewicz in_name = t->in_name ? t->in_name : t->filename;
42793f2aa4dSMichal Nazarewicz out_name = t->out_name ? t->out_name : t->filename;
42893f2aa4dSMichal Nazarewicz
42993f2aa4dSMichal Nazarewicz pthread_cleanup_push(cleanup_thread, arg);
43093f2aa4dSMichal Nazarewicz
43193f2aa4dSMichal Nazarewicz for (;;) {
43293f2aa4dSMichal Nazarewicz pthread_testcancel();
43393f2aa4dSMichal Nazarewicz
43493f2aa4dSMichal Nazarewicz ret = t->in(t, t->buf, t->buf_size);
43593f2aa4dSMichal Nazarewicz if (ret > 0) {
436eb9c5836SMatthias Fend ret = t->out(t, t->buf, ret);
43793f2aa4dSMichal Nazarewicz name = out_name;
43893f2aa4dSMichal Nazarewicz op = "write";
43993f2aa4dSMichal Nazarewicz } else {
44093f2aa4dSMichal Nazarewicz name = in_name;
44193f2aa4dSMichal Nazarewicz op = "read";
44293f2aa4dSMichal Nazarewicz }
44393f2aa4dSMichal Nazarewicz
44493f2aa4dSMichal Nazarewicz if (ret > 0) {
44593f2aa4dSMichal Nazarewicz /* nop */
44693f2aa4dSMichal Nazarewicz } else if (!ret) {
44793f2aa4dSMichal Nazarewicz debug("%s: %s: EOF", name, op);
44893f2aa4dSMichal Nazarewicz break;
44993f2aa4dSMichal Nazarewicz } else if (errno == EINTR || errno == EAGAIN) {
45093f2aa4dSMichal Nazarewicz debug("%s: %s", name, op);
45193f2aa4dSMichal Nazarewicz } else {
45293f2aa4dSMichal Nazarewicz warn("%s: %s", name, op);
45393f2aa4dSMichal Nazarewicz break;
45493f2aa4dSMichal Nazarewicz }
45593f2aa4dSMichal Nazarewicz }
45693f2aa4dSMichal Nazarewicz
45793f2aa4dSMichal Nazarewicz pthread_cleanup_pop(1);
45893f2aa4dSMichal Nazarewicz
45993f2aa4dSMichal Nazarewicz t->status = ret;
46093f2aa4dSMichal Nazarewicz info("%s: ends\n", t->filename);
46193f2aa4dSMichal Nazarewicz return NULL;
46293f2aa4dSMichal Nazarewicz }
46393f2aa4dSMichal Nazarewicz
start_thread(struct thread * t)46493f2aa4dSMichal Nazarewicz static void start_thread(struct thread *t)
46593f2aa4dSMichal Nazarewicz {
46693f2aa4dSMichal Nazarewicz debug("%s: starting\n", t->filename);
46793f2aa4dSMichal Nazarewicz
46893f2aa4dSMichal Nazarewicz die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0,
46993f2aa4dSMichal Nazarewicz "pthread_create(%s)", t->filename);
47093f2aa4dSMichal Nazarewicz }
47193f2aa4dSMichal Nazarewicz
join_thread(struct thread * t)47293f2aa4dSMichal Nazarewicz static void join_thread(struct thread *t)
47393f2aa4dSMichal Nazarewicz {
47493f2aa4dSMichal Nazarewicz int ret = pthread_join(t->id, NULL);
47593f2aa4dSMichal Nazarewicz
47693f2aa4dSMichal Nazarewicz if (ret < 0)
47793f2aa4dSMichal Nazarewicz err("%s: joining thread", t->filename);
47893f2aa4dSMichal Nazarewicz else
47993f2aa4dSMichal Nazarewicz debug("%s: joined\n", t->filename);
48093f2aa4dSMichal Nazarewicz }
48193f2aa4dSMichal Nazarewicz
48293f2aa4dSMichal Nazarewicz
read_wrap(struct thread * t,void * buf,size_t nbytes)48393f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
48493f2aa4dSMichal Nazarewicz {
48593f2aa4dSMichal Nazarewicz return read(t->fd, buf, nbytes);
48693f2aa4dSMichal Nazarewicz }
48793f2aa4dSMichal Nazarewicz
write_wrap(struct thread * t,const void * buf,size_t nbytes)48893f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes)
48993f2aa4dSMichal Nazarewicz {
49093f2aa4dSMichal Nazarewicz return write(t->fd, buf, nbytes);
49193f2aa4dSMichal Nazarewicz }
49293f2aa4dSMichal Nazarewicz
49393f2aa4dSMichal Nazarewicz
49493f2aa4dSMichal Nazarewicz /******************** Empty/Fill buffer routines ****************************/
49593f2aa4dSMichal Nazarewicz
49693f2aa4dSMichal Nazarewicz /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */
49793f2aa4dSMichal Nazarewicz enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
49893f2aa4dSMichal Nazarewicz static enum pattern pattern;
49993f2aa4dSMichal Nazarewicz
50093f2aa4dSMichal Nazarewicz static ssize_t
fill_in_buf(struct thread * ignore,void * buf,size_t nbytes)50193f2aa4dSMichal Nazarewicz fill_in_buf(struct thread *ignore, void *buf, size_t nbytes)
50293f2aa4dSMichal Nazarewicz {
50393f2aa4dSMichal Nazarewicz size_t i;
50493f2aa4dSMichal Nazarewicz __u8 *p;
50593f2aa4dSMichal Nazarewicz
50693f2aa4dSMichal Nazarewicz (void)ignore;
50793f2aa4dSMichal Nazarewicz
50893f2aa4dSMichal Nazarewicz switch (pattern) {
50993f2aa4dSMichal Nazarewicz case PAT_ZERO:
51093f2aa4dSMichal Nazarewicz memset(buf, 0, nbytes);
51193f2aa4dSMichal Nazarewicz break;
51293f2aa4dSMichal Nazarewicz
51393f2aa4dSMichal Nazarewicz case PAT_SEQ:
51493f2aa4dSMichal Nazarewicz for (p = buf, i = 0; i < nbytes; ++i, ++p)
51593f2aa4dSMichal Nazarewicz *p = i % 63;
51693f2aa4dSMichal Nazarewicz break;
51793f2aa4dSMichal Nazarewicz
51893f2aa4dSMichal Nazarewicz case PAT_PIPE:
51993f2aa4dSMichal Nazarewicz return fread(buf, 1, nbytes, stdin);
52093f2aa4dSMichal Nazarewicz }
52193f2aa4dSMichal Nazarewicz
52293f2aa4dSMichal Nazarewicz return nbytes;
52393f2aa4dSMichal Nazarewicz }
52493f2aa4dSMichal Nazarewicz
52593f2aa4dSMichal Nazarewicz static ssize_t
empty_out_buf(struct thread * ignore,const void * buf,size_t nbytes)52693f2aa4dSMichal Nazarewicz empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
52793f2aa4dSMichal Nazarewicz {
52893f2aa4dSMichal Nazarewicz const __u8 *p;
52993f2aa4dSMichal Nazarewicz __u8 expected;
53093f2aa4dSMichal Nazarewicz ssize_t ret;
53193f2aa4dSMichal Nazarewicz size_t len;
53293f2aa4dSMichal Nazarewicz
53393f2aa4dSMichal Nazarewicz (void)ignore;
53493f2aa4dSMichal Nazarewicz
53593f2aa4dSMichal Nazarewicz switch (pattern) {
53693f2aa4dSMichal Nazarewicz case PAT_ZERO:
53793f2aa4dSMichal Nazarewicz expected = 0;
53893f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len)
53993f2aa4dSMichal Nazarewicz if (*p)
54093f2aa4dSMichal Nazarewicz goto invalid;
54193f2aa4dSMichal Nazarewicz break;
54293f2aa4dSMichal Nazarewicz
54393f2aa4dSMichal Nazarewicz case PAT_SEQ:
54493f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len)
54593f2aa4dSMichal Nazarewicz if (*p != len % 63) {
54693f2aa4dSMichal Nazarewicz expected = len % 63;
54793f2aa4dSMichal Nazarewicz goto invalid;
54893f2aa4dSMichal Nazarewicz }
54993f2aa4dSMichal Nazarewicz break;
55093f2aa4dSMichal Nazarewicz
55193f2aa4dSMichal Nazarewicz case PAT_PIPE:
55293f2aa4dSMichal Nazarewicz ret = fwrite(buf, nbytes, 1, stdout);
55393f2aa4dSMichal Nazarewicz if (ret > 0)
55493f2aa4dSMichal Nazarewicz fflush(stdout);
55593f2aa4dSMichal Nazarewicz break;
55693f2aa4dSMichal Nazarewicz
55793f2aa4dSMichal Nazarewicz invalid:
55893f2aa4dSMichal Nazarewicz err("bad OUT byte %zd, expected %02x got %02x\n",
55993f2aa4dSMichal Nazarewicz len, expected, *p);
56093f2aa4dSMichal Nazarewicz for (p = buf, len = 0; len < nbytes; ++p, ++len) {
56193f2aa4dSMichal Nazarewicz if (0 == (len % 32))
562d105e74eSDavidlohr Bueso fprintf(stderr, "%4zd:", len);
56393f2aa4dSMichal Nazarewicz fprintf(stderr, " %02x", *p);
56493f2aa4dSMichal Nazarewicz if (31 == (len % 32))
56593f2aa4dSMichal Nazarewicz fprintf(stderr, "\n");
56693f2aa4dSMichal Nazarewicz }
56793f2aa4dSMichal Nazarewicz fflush(stderr);
56893f2aa4dSMichal Nazarewicz errno = EILSEQ;
56993f2aa4dSMichal Nazarewicz return -1;
57093f2aa4dSMichal Nazarewicz }
57193f2aa4dSMichal Nazarewicz
57293f2aa4dSMichal Nazarewicz return len;
57393f2aa4dSMichal Nazarewicz }
57493f2aa4dSMichal Nazarewicz
57593f2aa4dSMichal Nazarewicz
57693f2aa4dSMichal Nazarewicz /******************** Endpoints routines ************************************/
57793f2aa4dSMichal Nazarewicz
handle_setup(const struct usb_ctrlrequest * setup)57893f2aa4dSMichal Nazarewicz static void handle_setup(const struct usb_ctrlrequest *setup)
57993f2aa4dSMichal Nazarewicz {
58093f2aa4dSMichal Nazarewicz printf("bRequestType = %d\n", setup->bRequestType);
58193f2aa4dSMichal Nazarewicz printf("bRequest = %d\n", setup->bRequest);
58293f2aa4dSMichal Nazarewicz printf("wValue = %d\n", le16_to_cpu(setup->wValue));
58393f2aa4dSMichal Nazarewicz printf("wIndex = %d\n", le16_to_cpu(setup->wIndex));
58493f2aa4dSMichal Nazarewicz printf("wLength = %d\n", le16_to_cpu(setup->wLength));
58593f2aa4dSMichal Nazarewicz }
58693f2aa4dSMichal Nazarewicz
58793f2aa4dSMichal Nazarewicz static ssize_t
ep0_consume(struct thread * ignore,const void * buf,size_t nbytes)58893f2aa4dSMichal Nazarewicz ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
58993f2aa4dSMichal Nazarewicz {
59093f2aa4dSMichal Nazarewicz static const char *const names[] = {
59193f2aa4dSMichal Nazarewicz [FUNCTIONFS_BIND] = "BIND",
59293f2aa4dSMichal Nazarewicz [FUNCTIONFS_UNBIND] = "UNBIND",
59393f2aa4dSMichal Nazarewicz [FUNCTIONFS_ENABLE] = "ENABLE",
59493f2aa4dSMichal Nazarewicz [FUNCTIONFS_DISABLE] = "DISABLE",
59593f2aa4dSMichal Nazarewicz [FUNCTIONFS_SETUP] = "SETUP",
59693f2aa4dSMichal Nazarewicz [FUNCTIONFS_SUSPEND] = "SUSPEND",
59793f2aa4dSMichal Nazarewicz [FUNCTIONFS_RESUME] = "RESUME",
59893f2aa4dSMichal Nazarewicz };
59993f2aa4dSMichal Nazarewicz
60093f2aa4dSMichal Nazarewicz const struct usb_functionfs_event *event = buf;
60193f2aa4dSMichal Nazarewicz size_t n;
60293f2aa4dSMichal Nazarewicz
60393f2aa4dSMichal Nazarewicz (void)ignore;
60493f2aa4dSMichal Nazarewicz
60593f2aa4dSMichal Nazarewicz for (n = nbytes / sizeof *event; n; --n, ++event)
60693f2aa4dSMichal Nazarewicz switch (event->type) {
60793f2aa4dSMichal Nazarewicz case FUNCTIONFS_BIND:
60893f2aa4dSMichal Nazarewicz case FUNCTIONFS_UNBIND:
60993f2aa4dSMichal Nazarewicz case FUNCTIONFS_ENABLE:
61093f2aa4dSMichal Nazarewicz case FUNCTIONFS_DISABLE:
61193f2aa4dSMichal Nazarewicz case FUNCTIONFS_SETUP:
61293f2aa4dSMichal Nazarewicz case FUNCTIONFS_SUSPEND:
61393f2aa4dSMichal Nazarewicz case FUNCTIONFS_RESUME:
61493f2aa4dSMichal Nazarewicz printf("Event %s\n", names[event->type]);
61593f2aa4dSMichal Nazarewicz if (event->type == FUNCTIONFS_SETUP)
61693f2aa4dSMichal Nazarewicz handle_setup(&event->u.setup);
61793f2aa4dSMichal Nazarewicz break;
61893f2aa4dSMichal Nazarewicz
61993f2aa4dSMichal Nazarewicz default:
62093f2aa4dSMichal Nazarewicz printf("Event %03u (unknown)\n", event->type);
62193f2aa4dSMichal Nazarewicz }
62293f2aa4dSMichal Nazarewicz
62393f2aa4dSMichal Nazarewicz return nbytes;
62493f2aa4dSMichal Nazarewicz }
62593f2aa4dSMichal Nazarewicz
ep0_init(struct thread * t,bool legacy_descriptors)626b9a42746SMichal Nazarewicz static void ep0_init(struct thread *t, bool legacy_descriptors)
62793f2aa4dSMichal Nazarewicz {
628b9a42746SMichal Nazarewicz void *legacy;
62993f2aa4dSMichal Nazarewicz ssize_t ret;
630b9a42746SMichal Nazarewicz size_t len;
63193f2aa4dSMichal Nazarewicz
632b9a42746SMichal Nazarewicz if (legacy_descriptors) {
63393f2aa4dSMichal Nazarewicz info("%s: writing descriptors\n", t->filename);
634b9a42746SMichal Nazarewicz goto legacy;
635b9a42746SMichal Nazarewicz }
636b9a42746SMichal Nazarewicz
637b9a42746SMichal Nazarewicz info("%s: writing descriptors (in v2 format)\n", t->filename);
63893f2aa4dSMichal Nazarewicz ret = write(t->fd, &descriptors, sizeof descriptors);
639b9a42746SMichal Nazarewicz
640b9a42746SMichal Nazarewicz if (ret < 0 && errno == EINVAL) {
641b9a42746SMichal Nazarewicz warn("%s: new format rejected, trying legacy\n", t->filename);
642b9a42746SMichal Nazarewicz legacy:
643b9a42746SMichal Nazarewicz len = descs_to_legacy(&legacy, &descriptors);
644b9a42746SMichal Nazarewicz if (len) {
645b9a42746SMichal Nazarewicz ret = write(t->fd, legacy, len);
646b9a42746SMichal Nazarewicz free(legacy);
647b9a42746SMichal Nazarewicz }
648b9a42746SMichal Nazarewicz }
64993f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: descriptors", t->filename);
65093f2aa4dSMichal Nazarewicz
65193f2aa4dSMichal Nazarewicz info("%s: writing strings\n", t->filename);
65293f2aa4dSMichal Nazarewicz ret = write(t->fd, &strings, sizeof strings);
65393f2aa4dSMichal Nazarewicz die_on(ret < 0, "%s: write: strings", t->filename);
65493f2aa4dSMichal Nazarewicz }
65593f2aa4dSMichal Nazarewicz
65693f2aa4dSMichal Nazarewicz
65793f2aa4dSMichal Nazarewicz /******************** Main **************************************************/
65893f2aa4dSMichal Nazarewicz
main(int argc,char ** argv)659b9a42746SMichal Nazarewicz int main(int argc, char **argv)
66093f2aa4dSMichal Nazarewicz {
661b9a42746SMichal Nazarewicz bool legacy_descriptors;
66293f2aa4dSMichal Nazarewicz unsigned i;
66393f2aa4dSMichal Nazarewicz
664b9a42746SMichal Nazarewicz legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l");
66593f2aa4dSMichal Nazarewicz
66693f2aa4dSMichal Nazarewicz init_thread(threads);
667b9a42746SMichal Nazarewicz ep0_init(threads, legacy_descriptors);
66893f2aa4dSMichal Nazarewicz
66993f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i)
67093f2aa4dSMichal Nazarewicz init_thread(threads + i);
67193f2aa4dSMichal Nazarewicz
67293f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i)
67393f2aa4dSMichal Nazarewicz start_thread(threads + i);
67493f2aa4dSMichal Nazarewicz
67593f2aa4dSMichal Nazarewicz start_thread_helper(threads);
67693f2aa4dSMichal Nazarewicz
67793f2aa4dSMichal Nazarewicz for (i = 1; i < sizeof threads / sizeof *threads; ++i)
67893f2aa4dSMichal Nazarewicz join_thread(threads + i);
67993f2aa4dSMichal Nazarewicz
68093f2aa4dSMichal Nazarewicz return 0;
68193f2aa4dSMichal Nazarewicz }
682