xref: /openbmc/linux/tools/usb/ffs-test.c (revision a2b22dddc7bb6110ac3b5ed1a60aa9279836fadb)
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