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