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/* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */
29
30#define _DEFAULT_SOURCE /* for endian.h */
31
32#include <endian.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/ioctl.h>
40#include <sys/stat.h>
41#include <sys/types.h>
42#include <sys/poll.h>
43#include <unistd.h>
44#include <stdbool.h>
45#include <sys/eventfd.h>
46
47#include "libaio.h"
48#define IOCB_FLAG_RESFD         (1 << 0)
49
50#include <linux/usb/functionfs.h>
51
52#define BUF_LEN		8192
53
54/*
55 * cpu_to_le16/32 are used when initializing structures, a context where a
56 * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way
57 * that allows them to be used when initializing structures.
58 */
59
60#if BYTE_ORDER == __LITTLE_ENDIAN
61#define cpu_to_le16(x)  (x)
62#define cpu_to_le32(x)  (x)
63#else
64#define cpu_to_le16(x)  ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
65#define cpu_to_le32(x)  \
66	((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >>  8) | \
67	(((x) & 0x0000ff00u) <<  8) | (((x) & 0x000000ffu) << 24))
68#endif
69
70/******************** Descriptors and Strings *******************************/
71
72static const struct {
73	struct usb_functionfs_descs_head_v2 header;
74	__le32 fs_count;
75	__le32 hs_count;
76	struct {
77		struct usb_interface_descriptor intf;
78		struct usb_endpoint_descriptor_no_audio bulk_sink;
79		struct usb_endpoint_descriptor_no_audio bulk_source;
80	} __attribute__ ((__packed__)) fs_descs, hs_descs;
81} __attribute__ ((__packed__)) descriptors = {
82	.header = {
83		.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
84		.flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
85				     FUNCTIONFS_HAS_HS_DESC),
86		.length = cpu_to_le32(sizeof(descriptors)),
87	},
88	.fs_count = cpu_to_le32(3),
89	.fs_descs = {
90		.intf = {
91			.bLength = sizeof(descriptors.fs_descs.intf),
92			.bDescriptorType = USB_DT_INTERFACE,
93			.bNumEndpoints = 2,
94			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
95			.iInterface = 1,
96		},
97		.bulk_sink = {
98			.bLength = sizeof(descriptors.fs_descs.bulk_sink),
99			.bDescriptorType = USB_DT_ENDPOINT,
100			.bEndpointAddress = 1 | USB_DIR_IN,
101			.bmAttributes = USB_ENDPOINT_XFER_BULK,
102		},
103		.bulk_source = {
104			.bLength = sizeof(descriptors.fs_descs.bulk_source),
105			.bDescriptorType = USB_DT_ENDPOINT,
106			.bEndpointAddress = 2 | USB_DIR_OUT,
107			.bmAttributes = USB_ENDPOINT_XFER_BULK,
108		},
109	},
110	.hs_count = cpu_to_le32(3),
111	.hs_descs = {
112		.intf = {
113			.bLength = sizeof(descriptors.hs_descs.intf),
114			.bDescriptorType = USB_DT_INTERFACE,
115			.bNumEndpoints = 2,
116			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
117			.iInterface = 1,
118		},
119		.bulk_sink = {
120			.bLength = sizeof(descriptors.hs_descs.bulk_sink),
121			.bDescriptorType = USB_DT_ENDPOINT,
122			.bEndpointAddress = 1 | USB_DIR_IN,
123			.bmAttributes = USB_ENDPOINT_XFER_BULK,
124			.wMaxPacketSize = cpu_to_le16(512),
125		},
126		.bulk_source = {
127			.bLength = sizeof(descriptors.hs_descs.bulk_source),
128			.bDescriptorType = USB_DT_ENDPOINT,
129			.bEndpointAddress = 2 | USB_DIR_OUT,
130			.bmAttributes = USB_ENDPOINT_XFER_BULK,
131			.wMaxPacketSize = cpu_to_le16(512),
132		},
133	},
134};
135
136#define STR_INTERFACE "AIO Test"
137
138static const struct {
139	struct usb_functionfs_strings_head header;
140	struct {
141		__le16 code;
142		const char str1[sizeof(STR_INTERFACE)];
143	} __attribute__ ((__packed__)) lang0;
144} __attribute__ ((__packed__)) strings = {
145	.header = {
146		.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
147		.length = cpu_to_le32(sizeof(strings)),
148		.str_count = cpu_to_le32(1),
149		.lang_count = cpu_to_le32(1),
150	},
151	.lang0 = {
152		cpu_to_le16(0x0409), /* en-us */
153		STR_INTERFACE,
154	},
155};
156
157/******************** Endpoints handling *******************************/
158
159static void display_event(struct usb_functionfs_event *event)
160{
161	static const char *const names[] = {
162		[FUNCTIONFS_BIND] = "BIND",
163		[FUNCTIONFS_UNBIND] = "UNBIND",
164		[FUNCTIONFS_ENABLE] = "ENABLE",
165		[FUNCTIONFS_DISABLE] = "DISABLE",
166		[FUNCTIONFS_SETUP] = "SETUP",
167		[FUNCTIONFS_SUSPEND] = "SUSPEND",
168		[FUNCTIONFS_RESUME] = "RESUME",
169	};
170	switch (event->type) {
171	case FUNCTIONFS_BIND:
172	case FUNCTIONFS_UNBIND:
173	case FUNCTIONFS_ENABLE:
174	case FUNCTIONFS_DISABLE:
175	case FUNCTIONFS_SETUP:
176	case FUNCTIONFS_SUSPEND:
177	case FUNCTIONFS_RESUME:
178		printf("Event %s\n", names[event->type]);
179	}
180}
181
182static void handle_ep0(int ep0, bool *ready)
183{
184	struct usb_functionfs_event event;
185	int ret;
186
187	struct pollfd pfds[1];
188	pfds[0].fd = ep0;
189	pfds[0].events = POLLIN;
190
191	ret = poll(pfds, 1, 0);
192
193	if (ret && (pfds[0].revents & POLLIN)) {
194		ret = read(ep0, &event, sizeof(event));
195		if (!ret) {
196			perror("unable to read event from ep0");
197			return;
198		}
199		display_event(&event);
200		switch (event.type) {
201		case FUNCTIONFS_SETUP:
202			if (event.u.setup.bRequestType & USB_DIR_IN)
203				write(ep0, NULL, 0);
204			else
205				read(ep0, NULL, 0);
206			break;
207
208		case FUNCTIONFS_ENABLE:
209			*ready = true;
210			break;
211
212		case FUNCTIONFS_DISABLE:
213			*ready = false;
214			break;
215
216		default:
217			break;
218		}
219	}
220}
221
222int main(int argc, char *argv[])
223{
224	int i, ret;
225	char *ep_path;
226
227	int ep0;
228	int ep[2];
229
230	io_context_t ctx;
231
232	int evfd;
233	fd_set rfds;
234
235	char *buf_in, *buf_out;
236	struct iocb *iocb_in, *iocb_out;
237	int req_in = 0, req_out = 0;
238	bool ready;
239
240	if (argc != 2) {
241		printf("ffs directory not specified!\n");
242		return 1;
243	}
244
245	ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
246	if (!ep_path) {
247		perror("malloc");
248		return 1;
249	}
250
251	/* open endpoint files */
252	sprintf(ep_path, "%s/ep0", argv[1]);
253	ep0 = open(ep_path, O_RDWR);
254	if (ep0 < 0) {
255		perror("unable to open ep0");
256		return 1;
257	}
258	if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
259		perror("unable do write descriptors");
260		return 1;
261	}
262	if (write(ep0, &strings, sizeof(strings)) < 0) {
263		perror("unable to write strings");
264		return 1;
265	}
266	for (i = 0; i < 2; ++i) {
267		sprintf(ep_path, "%s/ep%d", argv[1], i+1);
268		ep[i] = open(ep_path, O_RDWR);
269		if (ep[i] < 0) {
270			printf("unable to open ep%d: %s\n", i+1,
271			       strerror(errno));
272			return 1;
273		}
274	}
275
276	free(ep_path);
277
278	memset(&ctx, 0, sizeof(ctx));
279	/* setup aio context to handle up to 2 requests */
280	if (io_setup(2, &ctx) < 0) {
281		perror("unable to setup aio");
282		return 1;
283	}
284
285	evfd = eventfd(0, 0);
286	if (evfd < 0) {
287		perror("unable to open eventfd");
288		return 1;
289	}
290
291	/* alloc buffers and requests */
292	buf_in = malloc(BUF_LEN);
293	buf_out = malloc(BUF_LEN);
294	iocb_in = malloc(sizeof(*iocb_in));
295	iocb_out = malloc(sizeof(*iocb_out));
296
297	while (1) {
298		FD_ZERO(&rfds);
299		FD_SET(ep0, &rfds);
300		FD_SET(evfd, &rfds);
301
302		ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
303			     &rfds, NULL, NULL, NULL);
304		if (ret < 0) {
305			if (errno == EINTR)
306				continue;
307			perror("select");
308			break;
309		}
310
311		if (FD_ISSET(ep0, &rfds))
312			handle_ep0(ep0, &ready);
313
314		/* we are waiting for function ENABLE */
315		if (!ready)
316			continue;
317
318		/* if something was submitted we wait for event */
319		if (FD_ISSET(evfd, &rfds)) {
320			uint64_t ev_cnt;
321			ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
322			if (ret < 0) {
323				perror("unable to read eventfd");
324				break;
325			}
326
327			struct io_event e[2];
328			/* we wait for one event */
329			ret = io_getevents(ctx, 1, 2, e, NULL);
330			/* if we got event */
331			for (i = 0; i < ret; ++i) {
332				if (e[i].obj->aio_fildes == ep[0]) {
333					printf("ev=in; ret=%lu\n", e[i].res);
334					req_in = 0;
335				} else if (e[i].obj->aio_fildes == ep[1]) {
336					printf("ev=out; ret=%lu\n", e[i].res);
337					req_out = 0;
338				}
339			}
340		}
341
342		if (!req_in) { /* if IN transfer not requested*/
343			/* prepare write request */
344			io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
345			/* enable eventfd notification */
346			iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
347			iocb_in->u.c.resfd = evfd;
348			/* submit table of requests */
349			ret = io_submit(ctx, 1, &iocb_in);
350			if (ret >= 0) { /* if ret > 0 request is queued */
351				req_in = 1;
352				printf("submit: in\n");
353			} else
354				perror("unable to submit request");
355		}
356		if (!req_out) { /* if OUT transfer not requested */
357			/* prepare read request */
358			io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
359			/* enable eventfs notification */
360			iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
361			iocb_out->u.c.resfd = evfd;
362			/* submit table of requests */
363			ret = io_submit(ctx, 1, &iocb_out);
364			if (ret >= 0) { /* if ret > 0 request is queued */
365				req_out = 1;
366				printf("submit: out\n");
367			} else
368				perror("unable to submit request");
369		}
370	}
371
372	/* free resources */
373
374	io_destroy(ctx);
375
376	free(buf_in);
377	free(buf_out);
378	free(iocb_in);
379	free(iocb_out);
380
381	for (i = 0; i < 2; ++i)
382		close(ep[i]);
383	close(ep0);
384
385	return 0;
386}
387