1/*
2 * This is free and unencumbered software released into the public domain.
3 *
4 * Anyone is free to copy, modify, publish, use, compile, sell, or
5 * distribute this software, either in source code form or as a compiled
6 * binary, for any purpose, commercial or non-commercial, and by any
7 * means.
8 *
9 * In jurisdictions that recognize copyright laws, the author or authors
10 * of this software dedicate any and all copyright interest in the
11 * software to the public domain. We make this dedication for the benefit
12 * of the public at large and to the detriment of our heirs and
13 * successors. We intend this dedication to be an overt act of
14 * relinquishment in perpetuity of all present and future rights to this
15 * software under copyright law.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * For more information, please refer to <http://unlicense.org/>
26 */
27
28#define _BSD_SOURCE /* for endian.h */
29
30#include <endian.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <stdarg.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <sys/poll.h>
41#include <unistd.h>
42#include <stdbool.h>
43#include <sys/eventfd.h>
44
45#include "libaio.h"
46#define IOCB_FLAG_RESFD         (1 << 0)
47
48#include <linux/usb/functionfs.h>
49
50#define BUF_LEN		8192
51
52/******************** Descriptors and Strings *******************************/
53
54static const struct {
55	struct usb_functionfs_descs_head_v2 header;
56	__le32 fs_count;
57	__le32 hs_count;
58	struct {
59		struct usb_interface_descriptor intf;
60		struct usb_endpoint_descriptor_no_audio bulk_sink;
61		struct usb_endpoint_descriptor_no_audio bulk_source;
62	} __attribute__ ((__packed__)) fs_descs, hs_descs;
63} __attribute__ ((__packed__)) descriptors = {
64	.header = {
65		.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
66		.flags = htole32(FUNCTIONFS_HAS_FS_DESC |
67				     FUNCTIONFS_HAS_HS_DESC),
68		.length = htole32(sizeof(descriptors)),
69	},
70	.fs_count = htole32(3),
71	.fs_descs = {
72		.intf = {
73			.bLength = sizeof(descriptors.fs_descs.intf),
74			.bDescriptorType = USB_DT_INTERFACE,
75			.bNumEndpoints = 2,
76			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
77			.iInterface = 1,
78		},
79		.bulk_sink = {
80			.bLength = sizeof(descriptors.fs_descs.bulk_sink),
81			.bDescriptorType = USB_DT_ENDPOINT,
82			.bEndpointAddress = 1 | USB_DIR_IN,
83			.bmAttributes = USB_ENDPOINT_XFER_BULK,
84		},
85		.bulk_source = {
86			.bLength = sizeof(descriptors.fs_descs.bulk_source),
87			.bDescriptorType = USB_DT_ENDPOINT,
88			.bEndpointAddress = 2 | USB_DIR_OUT,
89			.bmAttributes = USB_ENDPOINT_XFER_BULK,
90		},
91	},
92	.hs_count = htole32(3),
93	.hs_descs = {
94		.intf = {
95			.bLength = sizeof(descriptors.hs_descs.intf),
96			.bDescriptorType = USB_DT_INTERFACE,
97			.bNumEndpoints = 2,
98			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
99			.iInterface = 1,
100		},
101		.bulk_sink = {
102			.bLength = sizeof(descriptors.hs_descs.bulk_sink),
103			.bDescriptorType = USB_DT_ENDPOINT,
104			.bEndpointAddress = 1 | USB_DIR_IN,
105			.bmAttributes = USB_ENDPOINT_XFER_BULK,
106			.wMaxPacketSize = htole16(512),
107		},
108		.bulk_source = {
109			.bLength = sizeof(descriptors.hs_descs.bulk_source),
110			.bDescriptorType = USB_DT_ENDPOINT,
111			.bEndpointAddress = 2 | USB_DIR_OUT,
112			.bmAttributes = USB_ENDPOINT_XFER_BULK,
113			.wMaxPacketSize = htole16(512),
114		},
115	},
116};
117
118#define STR_INTERFACE "AIO Test"
119
120static const struct {
121	struct usb_functionfs_strings_head header;
122	struct {
123		__le16 code;
124		const char str1[sizeof(STR_INTERFACE)];
125	} __attribute__ ((__packed__)) lang0;
126} __attribute__ ((__packed__)) strings = {
127	.header = {
128		.magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
129		.length = htole32(sizeof(strings)),
130		.str_count = htole32(1),
131		.lang_count = htole32(1),
132	},
133	.lang0 = {
134		htole16(0x0409), /* en-us */
135		STR_INTERFACE,
136	},
137};
138
139/******************** Endpoints handling *******************************/
140
141static void display_event(struct usb_functionfs_event *event)
142{
143	static const char *const names[] = {
144		[FUNCTIONFS_BIND] = "BIND",
145		[FUNCTIONFS_UNBIND] = "UNBIND",
146		[FUNCTIONFS_ENABLE] = "ENABLE",
147		[FUNCTIONFS_DISABLE] = "DISABLE",
148		[FUNCTIONFS_SETUP] = "SETUP",
149		[FUNCTIONFS_SUSPEND] = "SUSPEND",
150		[FUNCTIONFS_RESUME] = "RESUME",
151	};
152	switch (event->type) {
153	case FUNCTIONFS_BIND:
154	case FUNCTIONFS_UNBIND:
155	case FUNCTIONFS_ENABLE:
156	case FUNCTIONFS_DISABLE:
157	case FUNCTIONFS_SETUP:
158	case FUNCTIONFS_SUSPEND:
159	case FUNCTIONFS_RESUME:
160		printf("Event %s\n", names[event->type]);
161	}
162}
163
164static void handle_ep0(int ep0, bool *ready)
165{
166	struct usb_functionfs_event event;
167	int ret;
168
169	struct pollfd pfds[1];
170	pfds[0].fd = ep0;
171	pfds[0].events = POLLIN;
172
173	ret = poll(pfds, 1, 0);
174
175	if (ret && (pfds[0].revents & POLLIN)) {
176		ret = read(ep0, &event, sizeof(event));
177		if (!ret) {
178			perror("unable to read event from ep0");
179			return;
180		}
181		display_event(&event);
182		switch (event.type) {
183		case FUNCTIONFS_SETUP:
184			if (event.u.setup.bRequestType & USB_DIR_IN)
185				write(ep0, NULL, 0);
186			else
187				read(ep0, NULL, 0);
188			break;
189
190		case FUNCTIONFS_ENABLE:
191			*ready = true;
192			break;
193
194		case FUNCTIONFS_DISABLE:
195			*ready = false;
196			break;
197
198		default:
199			break;
200		}
201	}
202}
203
204int main(int argc, char *argv[])
205{
206	int i, ret;
207	char *ep_path;
208
209	int ep0;
210	int ep[2];
211
212	io_context_t ctx;
213
214	int evfd;
215	fd_set rfds;
216
217	char *buf_in, *buf_out;
218	struct iocb *iocb_in, *iocb_out;
219	int req_in = 0, req_out = 0;
220	bool ready;
221
222	if (argc != 2) {
223		printf("ffs directory not specified!\n");
224		return 1;
225	}
226
227	ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
228	if (!ep_path) {
229		perror("malloc");
230		return 1;
231	}
232
233	/* open endpoint files */
234	sprintf(ep_path, "%s/ep0", argv[1]);
235	ep0 = open(ep_path, O_RDWR);
236	if (ep0 < 0) {
237		perror("unable to open ep0");
238		return 1;
239	}
240	if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
241		perror("unable do write descriptors");
242		return 1;
243	}
244	if (write(ep0, &strings, sizeof(strings)) < 0) {
245		perror("unable to write strings");
246		return 1;
247	}
248	for (i = 0; i < 2; ++i) {
249		sprintf(ep_path, "%s/ep%d", argv[1], i+1);
250		ep[i] = open(ep_path, O_RDWR);
251		if (ep[i] < 0) {
252			printf("unable to open ep%d: %s\n", i+1,
253			       strerror(errno));
254			return 1;
255		}
256	}
257
258	free(ep_path);
259
260	memset(&ctx, 0, sizeof(ctx));
261	/* setup aio context to handle up to 2 requests */
262	if (io_setup(2, &ctx) < 0) {
263		perror("unable to setup aio");
264		return 1;
265	}
266
267	evfd = eventfd(0, 0);
268	if (evfd < 0) {
269		perror("unable to open eventfd");
270		return 1;
271	}
272
273	/* alloc buffers and requests */
274	buf_in = malloc(BUF_LEN);
275	buf_out = malloc(BUF_LEN);
276	iocb_in = malloc(sizeof(*iocb_in));
277	iocb_out = malloc(sizeof(*iocb_out));
278
279	while (1) {
280		FD_ZERO(&rfds);
281		FD_SET(ep0, &rfds);
282		FD_SET(evfd, &rfds);
283
284		ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
285			     &rfds, NULL, NULL, NULL);
286		if (ret < 0) {
287			if (errno == EINTR)
288				continue;
289			perror("select");
290			break;
291		}
292
293		if (FD_ISSET(ep0, &rfds))
294			handle_ep0(ep0, &ready);
295
296		/* we are waiting for function ENABLE */
297		if (!ready)
298			continue;
299
300		/* if something was submitted we wait for event */
301		if (FD_ISSET(evfd, &rfds)) {
302			uint64_t ev_cnt;
303			ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
304			if (ret < 0) {
305				perror("unable to read eventfd");
306				break;
307			}
308
309			struct io_event e[2];
310			/* we wait for one event */
311			ret = io_getevents(ctx, 1, 2, e, NULL);
312			/* if we got event */
313			for (i = 0; i < ret; ++i) {
314				if (e[i].obj->aio_fildes == ep[0]) {
315					printf("ev=in; ret=%lu\n", e[i].res);
316					req_in = 0;
317				} else if (e[i].obj->aio_fildes == ep[1]) {
318					printf("ev=out; ret=%lu\n", e[i].res);
319					req_out = 0;
320				}
321			}
322		}
323
324		if (!req_in) { /* if IN transfer not requested*/
325			/* prepare write request */
326			io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
327			/* enable eventfd notification */
328			iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
329			iocb_in->u.c.resfd = evfd;
330			/* submit table of requests */
331			ret = io_submit(ctx, 1, &iocb_in);
332			if (ret >= 0) { /* if ret > 0 request is queued */
333				req_in = 1;
334				printf("submit: in\n");
335			} else
336				perror("unable to submit request");
337		}
338		if (!req_out) { /* if OUT transfer not requested */
339			/* prepare read request */
340			io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
341			/* enable eventfs notification */
342			iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
343			iocb_out->u.c.resfd = evfd;
344			/* submit table of requests */
345			ret = io_submit(ctx, 1, &iocb_out);
346			if (ret >= 0) { /* if ret > 0 request is queued */
347				req_out = 1;
348				printf("submit: out\n");
349			} else
350				perror("unable to submit request");
351		}
352	}
353
354	/* free resources */
355
356	io_destroy(ctx);
357
358	free(buf_in);
359	free(buf_out);
360	free(iocb_in);
361	free(iocb_out);
362
363	for (i = 0; i < 2; ++i)
364		close(ep[i]);
365	close(ep0);
366
367	return 0;
368}
369