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