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