xref: /openbmc/linux/tools/usb/ffs-test.c (revision 93f2aa4ddd25caac2b9a09538da54308dbda44e2)
1*93f2aa4dSMichal Nazarewicz /*
2*93f2aa4dSMichal Nazarewicz  * ffs-test.c.c -- user mode filesystem api for usb composite function
3*93f2aa4dSMichal Nazarewicz  *
4*93f2aa4dSMichal Nazarewicz  * Copyright (C) 2010 Samsung Electronics
5*93f2aa4dSMichal Nazarewicz  *                    Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
6*93f2aa4dSMichal Nazarewicz  *
7*93f2aa4dSMichal Nazarewicz  * This program is free software; you can redistribute it and/or modify
8*93f2aa4dSMichal Nazarewicz  * it under the terms of the GNU General Public License as published by
9*93f2aa4dSMichal Nazarewicz  * the Free Software Foundation; either version 2 of the License, or
10*93f2aa4dSMichal Nazarewicz  * (at your option) any later version.
11*93f2aa4dSMichal Nazarewicz  *
12*93f2aa4dSMichal Nazarewicz  * This program is distributed in the hope that it will be useful,
13*93f2aa4dSMichal Nazarewicz  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14*93f2aa4dSMichal Nazarewicz  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*93f2aa4dSMichal Nazarewicz  * GNU General Public License for more details.
16*93f2aa4dSMichal Nazarewicz  *
17*93f2aa4dSMichal Nazarewicz  * You should have received a copy of the GNU General Public License
18*93f2aa4dSMichal Nazarewicz  * along with this program; if not, write to the Free Software
19*93f2aa4dSMichal Nazarewicz  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20*93f2aa4dSMichal Nazarewicz  */
21*93f2aa4dSMichal Nazarewicz 
22*93f2aa4dSMichal Nazarewicz /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
23*93f2aa4dSMichal Nazarewicz 
24*93f2aa4dSMichal Nazarewicz 
25*93f2aa4dSMichal Nazarewicz #define _BSD_SOURCE /* for endian.h */
26*93f2aa4dSMichal Nazarewicz 
27*93f2aa4dSMichal Nazarewicz #include <endian.h>
28*93f2aa4dSMichal Nazarewicz #include <errno.h>
29*93f2aa4dSMichal Nazarewicz #include <fcntl.h>
30*93f2aa4dSMichal Nazarewicz #include <pthread.h>
31*93f2aa4dSMichal Nazarewicz #include <stdarg.h>
32*93f2aa4dSMichal Nazarewicz #include <stdio.h>
33*93f2aa4dSMichal Nazarewicz #include <stdlib.h>
34*93f2aa4dSMichal Nazarewicz #include <string.h>
35*93f2aa4dSMichal Nazarewicz #include <sys/ioctl.h>
36*93f2aa4dSMichal Nazarewicz #include <sys/stat.h>
37*93f2aa4dSMichal Nazarewicz #include <sys/types.h>
38*93f2aa4dSMichal Nazarewicz #include <unistd.h>
39*93f2aa4dSMichal Nazarewicz 
40*93f2aa4dSMichal Nazarewicz #include <linux/usb/functionfs.h>
41*93f2aa4dSMichal Nazarewicz 
42*93f2aa4dSMichal Nazarewicz 
43*93f2aa4dSMichal Nazarewicz /******************** Little Endian Handling ********************************/
44*93f2aa4dSMichal Nazarewicz 
45*93f2aa4dSMichal Nazarewicz #define cpu_to_le16(x)  htole16(x)
46*93f2aa4dSMichal Nazarewicz #define cpu_to_le32(x)  htole32(x)
47*93f2aa4dSMichal Nazarewicz #define le32_to_cpu(x)  le32toh(x)
48*93f2aa4dSMichal Nazarewicz #define le16_to_cpu(x)  le16toh(x)
49*93f2aa4dSMichal Nazarewicz 
50*93f2aa4dSMichal Nazarewicz static inline __u16 get_unaligned_le16(const void *_ptr)
51*93f2aa4dSMichal Nazarewicz {
52*93f2aa4dSMichal Nazarewicz 	const __u8 *ptr = _ptr;
53*93f2aa4dSMichal Nazarewicz 	return ptr[0] | (ptr[1] << 8);
54*93f2aa4dSMichal Nazarewicz }
55*93f2aa4dSMichal Nazarewicz 
56*93f2aa4dSMichal Nazarewicz static inline __u32 get_unaligned_le32(const void *_ptr)
57*93f2aa4dSMichal Nazarewicz {
58*93f2aa4dSMichal Nazarewicz 	const __u8 *ptr = _ptr;
59*93f2aa4dSMichal Nazarewicz 	return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
60*93f2aa4dSMichal Nazarewicz }
61*93f2aa4dSMichal Nazarewicz 
62*93f2aa4dSMichal Nazarewicz static inline void put_unaligned_le16(__u16 val, void *_ptr)
63*93f2aa4dSMichal Nazarewicz {
64*93f2aa4dSMichal Nazarewicz 	__u8 *ptr = _ptr;
65*93f2aa4dSMichal Nazarewicz 	*ptr++ = val;
66*93f2aa4dSMichal Nazarewicz 	*ptr++ = val >> 8;
67*93f2aa4dSMichal Nazarewicz }
68*93f2aa4dSMichal Nazarewicz 
69*93f2aa4dSMichal Nazarewicz static inline void put_unaligned_le32(__u32 val, void *_ptr)
70*93f2aa4dSMichal Nazarewicz {
71*93f2aa4dSMichal Nazarewicz 	__u8 *ptr = _ptr;
72*93f2aa4dSMichal Nazarewicz 	*ptr++ = val;
73*93f2aa4dSMichal Nazarewicz 	*ptr++ = val >>  8;
74*93f2aa4dSMichal Nazarewicz 	*ptr++ = val >> 16;
75*93f2aa4dSMichal Nazarewicz 	*ptr++ = val >> 24;
76*93f2aa4dSMichal Nazarewicz }
77*93f2aa4dSMichal Nazarewicz 
78*93f2aa4dSMichal Nazarewicz 
79*93f2aa4dSMichal Nazarewicz /******************** Messages and Errors ***********************************/
80*93f2aa4dSMichal Nazarewicz 
81*93f2aa4dSMichal Nazarewicz static const char argv0[] = "ffs-test";
82*93f2aa4dSMichal Nazarewicz 
83*93f2aa4dSMichal Nazarewicz static unsigned verbosity = 7;
84*93f2aa4dSMichal Nazarewicz 
85*93f2aa4dSMichal Nazarewicz static void _msg(unsigned level, const char *fmt, ...)
86*93f2aa4dSMichal Nazarewicz {
87*93f2aa4dSMichal Nazarewicz 	if (level < 2)
88*93f2aa4dSMichal Nazarewicz 		level = 2;
89*93f2aa4dSMichal Nazarewicz 	else if (level > 7)
90*93f2aa4dSMichal Nazarewicz 		level = 7;
91*93f2aa4dSMichal Nazarewicz 
92*93f2aa4dSMichal Nazarewicz 	if (level <= verbosity) {
93*93f2aa4dSMichal Nazarewicz 		static const char levels[8][6] = {
94*93f2aa4dSMichal Nazarewicz 			[2] = "crit:",
95*93f2aa4dSMichal Nazarewicz 			[3] = "err: ",
96*93f2aa4dSMichal Nazarewicz 			[4] = "warn:",
97*93f2aa4dSMichal Nazarewicz 			[5] = "note:",
98*93f2aa4dSMichal Nazarewicz 			[6] = "info:",
99*93f2aa4dSMichal Nazarewicz 			[7] = "dbg: "
100*93f2aa4dSMichal Nazarewicz 		};
101*93f2aa4dSMichal Nazarewicz 
102*93f2aa4dSMichal Nazarewicz 		int _errno = errno;
103*93f2aa4dSMichal Nazarewicz 		va_list ap;
104*93f2aa4dSMichal Nazarewicz 
105*93f2aa4dSMichal Nazarewicz 		fprintf(stderr, "%s: %s ", argv0, levels[level]);
106*93f2aa4dSMichal Nazarewicz 		va_start(ap, fmt);
107*93f2aa4dSMichal Nazarewicz 		vfprintf(stderr, fmt, ap);
108*93f2aa4dSMichal Nazarewicz 		va_end(ap);
109*93f2aa4dSMichal Nazarewicz 
110*93f2aa4dSMichal Nazarewicz 		if (fmt[strlen(fmt) - 1] != '\n') {
111*93f2aa4dSMichal Nazarewicz 			char buffer[128];
112*93f2aa4dSMichal Nazarewicz 			strerror_r(_errno, buffer, sizeof buffer);
113*93f2aa4dSMichal Nazarewicz 			fprintf(stderr, ": (-%d) %s\n", _errno, buffer);
114*93f2aa4dSMichal Nazarewicz 		}
115*93f2aa4dSMichal Nazarewicz 
116*93f2aa4dSMichal Nazarewicz 		fflush(stderr);
117*93f2aa4dSMichal Nazarewicz 	}
118*93f2aa4dSMichal Nazarewicz }
119*93f2aa4dSMichal Nazarewicz 
120*93f2aa4dSMichal Nazarewicz #define die(...)  (_msg(2, __VA_ARGS__), exit(1))
121*93f2aa4dSMichal Nazarewicz #define err(...)   _msg(3, __VA_ARGS__)
122*93f2aa4dSMichal Nazarewicz #define warn(...)  _msg(4, __VA_ARGS__)
123*93f2aa4dSMichal Nazarewicz #define note(...)  _msg(5, __VA_ARGS__)
124*93f2aa4dSMichal Nazarewicz #define info(...)  _msg(6, __VA_ARGS__)
125*93f2aa4dSMichal Nazarewicz #define debug(...) _msg(7, __VA_ARGS__)
126*93f2aa4dSMichal Nazarewicz 
127*93f2aa4dSMichal Nazarewicz #define die_on(cond, ...) do { \
128*93f2aa4dSMichal Nazarewicz 	if (cond) \
129*93f2aa4dSMichal Nazarewicz 		die(__VA_ARGS__); \
130*93f2aa4dSMichal Nazarewicz 	} while (0)
131*93f2aa4dSMichal Nazarewicz 
132*93f2aa4dSMichal Nazarewicz 
133*93f2aa4dSMichal Nazarewicz /******************** Descriptors and Strings *******************************/
134*93f2aa4dSMichal Nazarewicz 
135*93f2aa4dSMichal Nazarewicz static const struct {
136*93f2aa4dSMichal Nazarewicz 	struct usb_functionfs_descs_head header;
137*93f2aa4dSMichal Nazarewicz 	struct {
138*93f2aa4dSMichal Nazarewicz 		struct usb_interface_descriptor intf;
139*93f2aa4dSMichal Nazarewicz 		struct usb_endpoint_descriptor_no_audio sink;
140*93f2aa4dSMichal Nazarewicz 		struct usb_endpoint_descriptor_no_audio source;
141*93f2aa4dSMichal Nazarewicz 	} __attribute__((packed)) fs_descs, hs_descs;
142*93f2aa4dSMichal Nazarewicz } __attribute__((packed)) descriptors = {
143*93f2aa4dSMichal Nazarewicz 	.header = {
144*93f2aa4dSMichal Nazarewicz 		.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
145*93f2aa4dSMichal Nazarewicz 		.length = cpu_to_le32(sizeof descriptors),
146*93f2aa4dSMichal Nazarewicz 		.fs_count = 3,
147*93f2aa4dSMichal Nazarewicz 		.hs_count = 3,
148*93f2aa4dSMichal Nazarewicz 	},
149*93f2aa4dSMichal Nazarewicz 	.fs_descs = {
150*93f2aa4dSMichal Nazarewicz 		.intf = {
151*93f2aa4dSMichal Nazarewicz 			.bLength = sizeof descriptors.fs_descs.intf,
152*93f2aa4dSMichal Nazarewicz 			.bDescriptorType = USB_DT_INTERFACE,
153*93f2aa4dSMichal Nazarewicz 			.bNumEndpoints = 2,
154*93f2aa4dSMichal Nazarewicz 			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
155*93f2aa4dSMichal Nazarewicz 			.iInterface = 1,
156*93f2aa4dSMichal Nazarewicz 		},
157*93f2aa4dSMichal Nazarewicz 		.sink = {
158*93f2aa4dSMichal Nazarewicz 			.bLength = sizeof descriptors.fs_descs.sink,
159*93f2aa4dSMichal Nazarewicz 			.bDescriptorType = USB_DT_ENDPOINT,
160*93f2aa4dSMichal Nazarewicz 			.bEndpointAddress = 1 | USB_DIR_IN,
161*93f2aa4dSMichal Nazarewicz 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
162*93f2aa4dSMichal Nazarewicz 			/* .wMaxPacketSize = autoconfiguration (kernel) */
163*93f2aa4dSMichal Nazarewicz 		},
164*93f2aa4dSMichal Nazarewicz 		.source = {
165*93f2aa4dSMichal Nazarewicz 			.bLength = sizeof descriptors.fs_descs.source,
166*93f2aa4dSMichal Nazarewicz 			.bDescriptorType = USB_DT_ENDPOINT,
167*93f2aa4dSMichal Nazarewicz 			.bEndpointAddress = 2 | USB_DIR_OUT,
168*93f2aa4dSMichal Nazarewicz 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
169*93f2aa4dSMichal Nazarewicz 			/* .wMaxPacketSize = autoconfiguration (kernel) */
170*93f2aa4dSMichal Nazarewicz 		},
171*93f2aa4dSMichal Nazarewicz 	},
172*93f2aa4dSMichal Nazarewicz 	.hs_descs = {
173*93f2aa4dSMichal Nazarewicz 		.intf = {
174*93f2aa4dSMichal Nazarewicz 			.bLength = sizeof descriptors.fs_descs.intf,
175*93f2aa4dSMichal Nazarewicz 			.bDescriptorType = USB_DT_INTERFACE,
176*93f2aa4dSMichal Nazarewicz 			.bNumEndpoints = 2,
177*93f2aa4dSMichal Nazarewicz 			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
178*93f2aa4dSMichal Nazarewicz 			.iInterface = 1,
179*93f2aa4dSMichal Nazarewicz 		},
180*93f2aa4dSMichal Nazarewicz 		.sink = {
181*93f2aa4dSMichal Nazarewicz 			.bLength = sizeof descriptors.hs_descs.sink,
182*93f2aa4dSMichal Nazarewicz 			.bDescriptorType = USB_DT_ENDPOINT,
183*93f2aa4dSMichal Nazarewicz 			.bEndpointAddress = 1 | USB_DIR_IN,
184*93f2aa4dSMichal Nazarewicz 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
185*93f2aa4dSMichal Nazarewicz 			.wMaxPacketSize = cpu_to_le16(512),
186*93f2aa4dSMichal Nazarewicz 		},
187*93f2aa4dSMichal Nazarewicz 		.source = {
188*93f2aa4dSMichal Nazarewicz 			.bLength = sizeof descriptors.hs_descs.source,
189*93f2aa4dSMichal Nazarewicz 			.bDescriptorType = USB_DT_ENDPOINT,
190*93f2aa4dSMichal Nazarewicz 			.bEndpointAddress = 2 | USB_DIR_OUT,
191*93f2aa4dSMichal Nazarewicz 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
192*93f2aa4dSMichal Nazarewicz 			.wMaxPacketSize = cpu_to_le16(512),
193*93f2aa4dSMichal Nazarewicz 			.bInterval = 1, /* NAK every 1 uframe */
194*93f2aa4dSMichal Nazarewicz 		},
195*93f2aa4dSMichal Nazarewicz 	},
196*93f2aa4dSMichal Nazarewicz };
197*93f2aa4dSMichal Nazarewicz 
198*93f2aa4dSMichal Nazarewicz 
199*93f2aa4dSMichal Nazarewicz #define STR_INTERFACE_ "Source/Sink"
200*93f2aa4dSMichal Nazarewicz 
201*93f2aa4dSMichal Nazarewicz static const struct {
202*93f2aa4dSMichal Nazarewicz 	struct usb_functionfs_strings_head header;
203*93f2aa4dSMichal Nazarewicz 	struct {
204*93f2aa4dSMichal Nazarewicz 		__le16 code;
205*93f2aa4dSMichal Nazarewicz 		const char str1[sizeof STR_INTERFACE_];
206*93f2aa4dSMichal Nazarewicz 	} __attribute__((packed)) lang0;
207*93f2aa4dSMichal Nazarewicz } __attribute__((packed)) strings = {
208*93f2aa4dSMichal Nazarewicz 	.header = {
209*93f2aa4dSMichal Nazarewicz 		.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
210*93f2aa4dSMichal Nazarewicz 		.length = cpu_to_le32(sizeof strings),
211*93f2aa4dSMichal Nazarewicz 		.str_count = cpu_to_le32(1),
212*93f2aa4dSMichal Nazarewicz 		.lang_count = cpu_to_le32(1),
213*93f2aa4dSMichal Nazarewicz 	},
214*93f2aa4dSMichal Nazarewicz 	.lang0 = {
215*93f2aa4dSMichal Nazarewicz 		cpu_to_le16(0x0409), /* en-us */
216*93f2aa4dSMichal Nazarewicz 		STR_INTERFACE_,
217*93f2aa4dSMichal Nazarewicz 	},
218*93f2aa4dSMichal Nazarewicz };
219*93f2aa4dSMichal Nazarewicz 
220*93f2aa4dSMichal Nazarewicz #define STR_INTERFACE strings.lang0.str1
221*93f2aa4dSMichal Nazarewicz 
222*93f2aa4dSMichal Nazarewicz 
223*93f2aa4dSMichal Nazarewicz /******************** Files and Threads Handling ****************************/
224*93f2aa4dSMichal Nazarewicz 
225*93f2aa4dSMichal Nazarewicz struct thread;
226*93f2aa4dSMichal Nazarewicz 
227*93f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
228*93f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
229*93f2aa4dSMichal Nazarewicz static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
230*93f2aa4dSMichal Nazarewicz static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
231*93f2aa4dSMichal Nazarewicz static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
232*93f2aa4dSMichal Nazarewicz 
233*93f2aa4dSMichal Nazarewicz 
234*93f2aa4dSMichal Nazarewicz static struct thread {
235*93f2aa4dSMichal Nazarewicz 	const char *const filename;
236*93f2aa4dSMichal Nazarewicz 	size_t buf_size;
237*93f2aa4dSMichal Nazarewicz 
238*93f2aa4dSMichal Nazarewicz 	ssize_t (*in)(struct thread *, void *, size_t);
239*93f2aa4dSMichal Nazarewicz 	const char *const in_name;
240*93f2aa4dSMichal Nazarewicz 
241*93f2aa4dSMichal Nazarewicz 	ssize_t (*out)(struct thread *, const void *, size_t);
242*93f2aa4dSMichal Nazarewicz 	const char *const out_name;
243*93f2aa4dSMichal Nazarewicz 
244*93f2aa4dSMichal Nazarewicz 	int fd;
245*93f2aa4dSMichal Nazarewicz 	pthread_t id;
246*93f2aa4dSMichal Nazarewicz 	void *buf;
247*93f2aa4dSMichal Nazarewicz 	ssize_t status;
248*93f2aa4dSMichal Nazarewicz } threads[] = {
249*93f2aa4dSMichal Nazarewicz 	{
250*93f2aa4dSMichal Nazarewicz 		"ep0", 4 * sizeof(struct usb_functionfs_event),
251*93f2aa4dSMichal Nazarewicz 		read_wrap, NULL,
252*93f2aa4dSMichal Nazarewicz 		ep0_consume, "<consume>",
253*93f2aa4dSMichal Nazarewicz 		0, 0, NULL, 0
254*93f2aa4dSMichal Nazarewicz 	},
255*93f2aa4dSMichal Nazarewicz 	{
256*93f2aa4dSMichal Nazarewicz 		"ep1", 8 * 1024,
257*93f2aa4dSMichal Nazarewicz 		fill_in_buf, "<in>",
258*93f2aa4dSMichal Nazarewicz 		write_wrap, NULL,
259*93f2aa4dSMichal Nazarewicz 		0, 0, NULL, 0
260*93f2aa4dSMichal Nazarewicz 	},
261*93f2aa4dSMichal Nazarewicz 	{
262*93f2aa4dSMichal Nazarewicz 		"ep2", 8 * 1024,
263*93f2aa4dSMichal Nazarewicz 		read_wrap, NULL,
264*93f2aa4dSMichal Nazarewicz 		empty_out_buf, "<out>",
265*93f2aa4dSMichal Nazarewicz 		0, 0, NULL, 0
266*93f2aa4dSMichal Nazarewicz 	},
267*93f2aa4dSMichal Nazarewicz };
268*93f2aa4dSMichal Nazarewicz 
269*93f2aa4dSMichal Nazarewicz 
270*93f2aa4dSMichal Nazarewicz static void init_thread(struct thread *t)
271*93f2aa4dSMichal Nazarewicz {
272*93f2aa4dSMichal Nazarewicz 	t->buf = malloc(t->buf_size);
273*93f2aa4dSMichal Nazarewicz 	die_on(!t->buf, "malloc");
274*93f2aa4dSMichal Nazarewicz 
275*93f2aa4dSMichal Nazarewicz 	t->fd = open(t->filename, O_RDWR);
276*93f2aa4dSMichal Nazarewicz 	die_on(t->fd < 0, "%s", t->filename);
277*93f2aa4dSMichal Nazarewicz }
278*93f2aa4dSMichal Nazarewicz 
279*93f2aa4dSMichal Nazarewicz static void cleanup_thread(void *arg)
280*93f2aa4dSMichal Nazarewicz {
281*93f2aa4dSMichal Nazarewicz 	struct thread *t = arg;
282*93f2aa4dSMichal Nazarewicz 	int ret, fd;
283*93f2aa4dSMichal Nazarewicz 
284*93f2aa4dSMichal Nazarewicz 	fd = t->fd;
285*93f2aa4dSMichal Nazarewicz 	if (t->fd < 0)
286*93f2aa4dSMichal Nazarewicz 		return;
287*93f2aa4dSMichal Nazarewicz 	t->fd = -1;
288*93f2aa4dSMichal Nazarewicz 
289*93f2aa4dSMichal Nazarewicz 	/* test the FIFO ioctls (non-ep0 code paths) */
290*93f2aa4dSMichal Nazarewicz 	if (t != threads) {
291*93f2aa4dSMichal Nazarewicz 		ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS);
292*93f2aa4dSMichal Nazarewicz 		if (ret < 0) {
293*93f2aa4dSMichal Nazarewicz 			/* ENODEV reported after disconnect */
294*93f2aa4dSMichal Nazarewicz 			if (errno != ENODEV)
295*93f2aa4dSMichal Nazarewicz 				err("%s: get fifo status", t->filename);
296*93f2aa4dSMichal Nazarewicz 		} else if (ret) {
297*93f2aa4dSMichal Nazarewicz 			warn("%s: unclaimed = %d\n", t->filename, ret);
298*93f2aa4dSMichal Nazarewicz 			if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0)
299*93f2aa4dSMichal Nazarewicz 				err("%s: fifo flush", t->filename);
300*93f2aa4dSMichal Nazarewicz 		}
301*93f2aa4dSMichal Nazarewicz 	}
302*93f2aa4dSMichal Nazarewicz 
303*93f2aa4dSMichal Nazarewicz 	if (close(fd) < 0)
304*93f2aa4dSMichal Nazarewicz 		err("%s: close", t->filename);
305*93f2aa4dSMichal Nazarewicz 
306*93f2aa4dSMichal Nazarewicz 	free(t->buf);
307*93f2aa4dSMichal Nazarewicz 	t->buf = NULL;
308*93f2aa4dSMichal Nazarewicz }
309*93f2aa4dSMichal Nazarewicz 
310*93f2aa4dSMichal Nazarewicz static void *start_thread_helper(void *arg)
311*93f2aa4dSMichal Nazarewicz {
312*93f2aa4dSMichal Nazarewicz 	const char *name, *op, *in_name, *out_name;
313*93f2aa4dSMichal Nazarewicz 	struct thread *t = arg;
314*93f2aa4dSMichal Nazarewicz 	ssize_t ret;
315*93f2aa4dSMichal Nazarewicz 
316*93f2aa4dSMichal Nazarewicz 	info("%s: starts\n", t->filename);
317*93f2aa4dSMichal Nazarewicz 	in_name = t->in_name ? t->in_name : t->filename;
318*93f2aa4dSMichal Nazarewicz 	out_name = t->out_name ? t->out_name : t->filename;
319*93f2aa4dSMichal Nazarewicz 
320*93f2aa4dSMichal Nazarewicz 	pthread_cleanup_push(cleanup_thread, arg);
321*93f2aa4dSMichal Nazarewicz 
322*93f2aa4dSMichal Nazarewicz 	for (;;) {
323*93f2aa4dSMichal Nazarewicz 		pthread_testcancel();
324*93f2aa4dSMichal Nazarewicz 
325*93f2aa4dSMichal Nazarewicz 		ret = t->in(t, t->buf, t->buf_size);
326*93f2aa4dSMichal Nazarewicz 		if (ret > 0) {
327*93f2aa4dSMichal Nazarewicz 			ret = t->out(t, t->buf, t->buf_size);
328*93f2aa4dSMichal Nazarewicz 			name = out_name;
329*93f2aa4dSMichal Nazarewicz 			op = "write";
330*93f2aa4dSMichal Nazarewicz 		} else {
331*93f2aa4dSMichal Nazarewicz 			name = in_name;
332*93f2aa4dSMichal Nazarewicz 			op = "read";
333*93f2aa4dSMichal Nazarewicz 		}
334*93f2aa4dSMichal Nazarewicz 
335*93f2aa4dSMichal Nazarewicz 		if (ret > 0) {
336*93f2aa4dSMichal Nazarewicz 			/* nop */
337*93f2aa4dSMichal Nazarewicz 		} else if (!ret) {
338*93f2aa4dSMichal Nazarewicz 			debug("%s: %s: EOF", name, op);
339*93f2aa4dSMichal Nazarewicz 			break;
340*93f2aa4dSMichal Nazarewicz 		} else if (errno == EINTR || errno == EAGAIN) {
341*93f2aa4dSMichal Nazarewicz 			debug("%s: %s", name, op);
342*93f2aa4dSMichal Nazarewicz 		} else {
343*93f2aa4dSMichal Nazarewicz 			warn("%s: %s", name, op);
344*93f2aa4dSMichal Nazarewicz 			break;
345*93f2aa4dSMichal Nazarewicz 		}
346*93f2aa4dSMichal Nazarewicz 	}
347*93f2aa4dSMichal Nazarewicz 
348*93f2aa4dSMichal Nazarewicz 	pthread_cleanup_pop(1);
349*93f2aa4dSMichal Nazarewicz 
350*93f2aa4dSMichal Nazarewicz 	t->status = ret;
351*93f2aa4dSMichal Nazarewicz 	info("%s: ends\n", t->filename);
352*93f2aa4dSMichal Nazarewicz 	return NULL;
353*93f2aa4dSMichal Nazarewicz }
354*93f2aa4dSMichal Nazarewicz 
355*93f2aa4dSMichal Nazarewicz static void start_thread(struct thread *t)
356*93f2aa4dSMichal Nazarewicz {
357*93f2aa4dSMichal Nazarewicz 	debug("%s: starting\n", t->filename);
358*93f2aa4dSMichal Nazarewicz 
359*93f2aa4dSMichal Nazarewicz 	die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0,
360*93f2aa4dSMichal Nazarewicz 	       "pthread_create(%s)", t->filename);
361*93f2aa4dSMichal Nazarewicz }
362*93f2aa4dSMichal Nazarewicz 
363*93f2aa4dSMichal Nazarewicz static void join_thread(struct thread *t)
364*93f2aa4dSMichal Nazarewicz {
365*93f2aa4dSMichal Nazarewicz 	int ret = pthread_join(t->id, NULL);
366*93f2aa4dSMichal Nazarewicz 
367*93f2aa4dSMichal Nazarewicz 	if (ret < 0)
368*93f2aa4dSMichal Nazarewicz 		err("%s: joining thread", t->filename);
369*93f2aa4dSMichal Nazarewicz 	else
370*93f2aa4dSMichal Nazarewicz 		debug("%s: joined\n", t->filename);
371*93f2aa4dSMichal Nazarewicz }
372*93f2aa4dSMichal Nazarewicz 
373*93f2aa4dSMichal Nazarewicz 
374*93f2aa4dSMichal Nazarewicz static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
375*93f2aa4dSMichal Nazarewicz {
376*93f2aa4dSMichal Nazarewicz 	return read(t->fd, buf, nbytes);
377*93f2aa4dSMichal Nazarewicz }
378*93f2aa4dSMichal Nazarewicz 
379*93f2aa4dSMichal Nazarewicz static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes)
380*93f2aa4dSMichal Nazarewicz {
381*93f2aa4dSMichal Nazarewicz 	return write(t->fd, buf, nbytes);
382*93f2aa4dSMichal Nazarewicz }
383*93f2aa4dSMichal Nazarewicz 
384*93f2aa4dSMichal Nazarewicz 
385*93f2aa4dSMichal Nazarewicz /******************** Empty/Fill buffer routines ****************************/
386*93f2aa4dSMichal Nazarewicz 
387*93f2aa4dSMichal Nazarewicz /* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */
388*93f2aa4dSMichal Nazarewicz enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
389*93f2aa4dSMichal Nazarewicz static enum pattern pattern;
390*93f2aa4dSMichal Nazarewicz 
391*93f2aa4dSMichal Nazarewicz static ssize_t
392*93f2aa4dSMichal Nazarewicz fill_in_buf(struct thread *ignore, void *buf, size_t nbytes)
393*93f2aa4dSMichal Nazarewicz {
394*93f2aa4dSMichal Nazarewicz 	size_t i;
395*93f2aa4dSMichal Nazarewicz 	__u8 *p;
396*93f2aa4dSMichal Nazarewicz 
397*93f2aa4dSMichal Nazarewicz 	(void)ignore;
398*93f2aa4dSMichal Nazarewicz 
399*93f2aa4dSMichal Nazarewicz 	switch (pattern) {
400*93f2aa4dSMichal Nazarewicz 	case PAT_ZERO:
401*93f2aa4dSMichal Nazarewicz 		memset(buf, 0, nbytes);
402*93f2aa4dSMichal Nazarewicz 		break;
403*93f2aa4dSMichal Nazarewicz 
404*93f2aa4dSMichal Nazarewicz 	case PAT_SEQ:
405*93f2aa4dSMichal Nazarewicz 		for (p = buf, i = 0; i < nbytes; ++i, ++p)
406*93f2aa4dSMichal Nazarewicz 			*p = i % 63;
407*93f2aa4dSMichal Nazarewicz 		break;
408*93f2aa4dSMichal Nazarewicz 
409*93f2aa4dSMichal Nazarewicz 	case PAT_PIPE:
410*93f2aa4dSMichal Nazarewicz 		return fread(buf, 1, nbytes, stdin);
411*93f2aa4dSMichal Nazarewicz 	}
412*93f2aa4dSMichal Nazarewicz 
413*93f2aa4dSMichal Nazarewicz 	return nbytes;
414*93f2aa4dSMichal Nazarewicz }
415*93f2aa4dSMichal Nazarewicz 
416*93f2aa4dSMichal Nazarewicz static ssize_t
417*93f2aa4dSMichal Nazarewicz empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
418*93f2aa4dSMichal Nazarewicz {
419*93f2aa4dSMichal Nazarewicz 	const __u8 *p;
420*93f2aa4dSMichal Nazarewicz 	__u8 expected;
421*93f2aa4dSMichal Nazarewicz 	ssize_t ret;
422*93f2aa4dSMichal Nazarewicz 	size_t len;
423*93f2aa4dSMichal Nazarewicz 
424*93f2aa4dSMichal Nazarewicz 	(void)ignore;
425*93f2aa4dSMichal Nazarewicz 
426*93f2aa4dSMichal Nazarewicz 	switch (pattern) {
427*93f2aa4dSMichal Nazarewicz 	case PAT_ZERO:
428*93f2aa4dSMichal Nazarewicz 		expected = 0;
429*93f2aa4dSMichal Nazarewicz 		for (p = buf, len = 0; len < nbytes; ++p, ++len)
430*93f2aa4dSMichal Nazarewicz 			if (*p)
431*93f2aa4dSMichal Nazarewicz 				goto invalid;
432*93f2aa4dSMichal Nazarewicz 		break;
433*93f2aa4dSMichal Nazarewicz 
434*93f2aa4dSMichal Nazarewicz 	case PAT_SEQ:
435*93f2aa4dSMichal Nazarewicz 		for (p = buf, len = 0; len < nbytes; ++p, ++len)
436*93f2aa4dSMichal Nazarewicz 			if (*p != len % 63) {
437*93f2aa4dSMichal Nazarewicz 				expected = len % 63;
438*93f2aa4dSMichal Nazarewicz 				goto invalid;
439*93f2aa4dSMichal Nazarewicz 			}
440*93f2aa4dSMichal Nazarewicz 		break;
441*93f2aa4dSMichal Nazarewicz 
442*93f2aa4dSMichal Nazarewicz 	case PAT_PIPE:
443*93f2aa4dSMichal Nazarewicz 		ret = fwrite(buf, nbytes, 1, stdout);
444*93f2aa4dSMichal Nazarewicz 		if (ret > 0)
445*93f2aa4dSMichal Nazarewicz 			fflush(stdout);
446*93f2aa4dSMichal Nazarewicz 		break;
447*93f2aa4dSMichal Nazarewicz 
448*93f2aa4dSMichal Nazarewicz invalid:
449*93f2aa4dSMichal Nazarewicz 		err("bad OUT byte %zd, expected %02x got %02x\n",
450*93f2aa4dSMichal Nazarewicz 		    len, expected, *p);
451*93f2aa4dSMichal Nazarewicz 		for (p = buf, len = 0; len < nbytes; ++p, ++len) {
452*93f2aa4dSMichal Nazarewicz 			if (0 == (len % 32))
453*93f2aa4dSMichal Nazarewicz 				fprintf(stderr, "%4d:", len);
454*93f2aa4dSMichal Nazarewicz 			fprintf(stderr, " %02x", *p);
455*93f2aa4dSMichal Nazarewicz 			if (31 == (len % 32))
456*93f2aa4dSMichal Nazarewicz 				fprintf(stderr, "\n");
457*93f2aa4dSMichal Nazarewicz 		}
458*93f2aa4dSMichal Nazarewicz 		fflush(stderr);
459*93f2aa4dSMichal Nazarewicz 		errno = EILSEQ;
460*93f2aa4dSMichal Nazarewicz 		return -1;
461*93f2aa4dSMichal Nazarewicz 	}
462*93f2aa4dSMichal Nazarewicz 
463*93f2aa4dSMichal Nazarewicz 	return len;
464*93f2aa4dSMichal Nazarewicz }
465*93f2aa4dSMichal Nazarewicz 
466*93f2aa4dSMichal Nazarewicz 
467*93f2aa4dSMichal Nazarewicz /******************** Endpoints routines ************************************/
468*93f2aa4dSMichal Nazarewicz 
469*93f2aa4dSMichal Nazarewicz static void handle_setup(const struct usb_ctrlrequest *setup)
470*93f2aa4dSMichal Nazarewicz {
471*93f2aa4dSMichal Nazarewicz 	printf("bRequestType = %d\n", setup->bRequestType);
472*93f2aa4dSMichal Nazarewicz 	printf("bRequest     = %d\n", setup->bRequest);
473*93f2aa4dSMichal Nazarewicz 	printf("wValue       = %d\n", le16_to_cpu(setup->wValue));
474*93f2aa4dSMichal Nazarewicz 	printf("wIndex       = %d\n", le16_to_cpu(setup->wIndex));
475*93f2aa4dSMichal Nazarewicz 	printf("wLength      = %d\n", le16_to_cpu(setup->wLength));
476*93f2aa4dSMichal Nazarewicz }
477*93f2aa4dSMichal Nazarewicz 
478*93f2aa4dSMichal Nazarewicz static ssize_t
479*93f2aa4dSMichal Nazarewicz ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
480*93f2aa4dSMichal Nazarewicz {
481*93f2aa4dSMichal Nazarewicz 	static const char *const names[] = {
482*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_BIND] = "BIND",
483*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_UNBIND] = "UNBIND",
484*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_ENABLE] = "ENABLE",
485*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_DISABLE] = "DISABLE",
486*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_SETUP] = "SETUP",
487*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_SUSPEND] = "SUSPEND",
488*93f2aa4dSMichal Nazarewicz 		[FUNCTIONFS_RESUME] = "RESUME",
489*93f2aa4dSMichal Nazarewicz 	};
490*93f2aa4dSMichal Nazarewicz 
491*93f2aa4dSMichal Nazarewicz 	const struct usb_functionfs_event *event = buf;
492*93f2aa4dSMichal Nazarewicz 	size_t n;
493*93f2aa4dSMichal Nazarewicz 
494*93f2aa4dSMichal Nazarewicz 	(void)ignore;
495*93f2aa4dSMichal Nazarewicz 
496*93f2aa4dSMichal Nazarewicz 	for (n = nbytes / sizeof *event; n; --n, ++event)
497*93f2aa4dSMichal Nazarewicz 		switch (event->type) {
498*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_BIND:
499*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_UNBIND:
500*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_ENABLE:
501*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_DISABLE:
502*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_SETUP:
503*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_SUSPEND:
504*93f2aa4dSMichal Nazarewicz 		case FUNCTIONFS_RESUME:
505*93f2aa4dSMichal Nazarewicz 			printf("Event %s\n", names[event->type]);
506*93f2aa4dSMichal Nazarewicz 			if (event->type == FUNCTIONFS_SETUP)
507*93f2aa4dSMichal Nazarewicz 				handle_setup(&event->u.setup);
508*93f2aa4dSMichal Nazarewicz 			break;
509*93f2aa4dSMichal Nazarewicz 
510*93f2aa4dSMichal Nazarewicz 		default:
511*93f2aa4dSMichal Nazarewicz 			printf("Event %03u (unknown)\n", event->type);
512*93f2aa4dSMichal Nazarewicz 		}
513*93f2aa4dSMichal Nazarewicz 
514*93f2aa4dSMichal Nazarewicz 	return nbytes;
515*93f2aa4dSMichal Nazarewicz }
516*93f2aa4dSMichal Nazarewicz 
517*93f2aa4dSMichal Nazarewicz static void ep0_init(struct thread *t)
518*93f2aa4dSMichal Nazarewicz {
519*93f2aa4dSMichal Nazarewicz 	ssize_t ret;
520*93f2aa4dSMichal Nazarewicz 
521*93f2aa4dSMichal Nazarewicz 	info("%s: writing descriptors\n", t->filename);
522*93f2aa4dSMichal Nazarewicz 	ret = write(t->fd, &descriptors, sizeof descriptors);
523*93f2aa4dSMichal Nazarewicz 	die_on(ret < 0, "%s: write: descriptors", t->filename);
524*93f2aa4dSMichal Nazarewicz 
525*93f2aa4dSMichal Nazarewicz 	info("%s: writing strings\n", t->filename);
526*93f2aa4dSMichal Nazarewicz 	ret = write(t->fd, &strings, sizeof strings);
527*93f2aa4dSMichal Nazarewicz 	die_on(ret < 0, "%s: write: strings", t->filename);
528*93f2aa4dSMichal Nazarewicz }
529*93f2aa4dSMichal Nazarewicz 
530*93f2aa4dSMichal Nazarewicz 
531*93f2aa4dSMichal Nazarewicz /******************** Main **************************************************/
532*93f2aa4dSMichal Nazarewicz 
533*93f2aa4dSMichal Nazarewicz int main(void)
534*93f2aa4dSMichal Nazarewicz {
535*93f2aa4dSMichal Nazarewicz 	unsigned i;
536*93f2aa4dSMichal Nazarewicz 
537*93f2aa4dSMichal Nazarewicz 	/* XXX TODO: Argument parsing missing */
538*93f2aa4dSMichal Nazarewicz 
539*93f2aa4dSMichal Nazarewicz 	init_thread(threads);
540*93f2aa4dSMichal Nazarewicz 	ep0_init(threads);
541*93f2aa4dSMichal Nazarewicz 
542*93f2aa4dSMichal Nazarewicz 	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
543*93f2aa4dSMichal Nazarewicz 		init_thread(threads + i);
544*93f2aa4dSMichal Nazarewicz 
545*93f2aa4dSMichal Nazarewicz 	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
546*93f2aa4dSMichal Nazarewicz 		start_thread(threads + i);
547*93f2aa4dSMichal Nazarewicz 
548*93f2aa4dSMichal Nazarewicz 	start_thread_helper(threads);
549*93f2aa4dSMichal Nazarewicz 
550*93f2aa4dSMichal Nazarewicz 	for (i = 1; i < sizeof threads / sizeof *threads; ++i)
551*93f2aa4dSMichal Nazarewicz 		join_thread(threads + i);
552*93f2aa4dSMichal Nazarewicz 
553*93f2aa4dSMichal Nazarewicz 	return 0;
554*93f2aa4dSMichal Nazarewicz }
555