xref: /OK3568_Linux_fs/kernel/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 
54 static const struct {
55 	struct usb_functionfs_descs_head_v2 header;
56 	__le32 fs_count;
57 	__le32 hs_count;
58 	__le32 ss_count;
59 	__le32 os_count;
60 	struct {
61 		struct usb_interface_descriptor intf;
62 		struct usb_endpoint_descriptor_no_audio bulk_sink;
63 		struct usb_endpoint_descriptor_no_audio bulk_source;
64 	} __attribute__ ((__packed__)) fs_descs, hs_descs;
65 	struct {
66 		struct usb_interface_descriptor intf;
67 		struct usb_endpoint_descriptor_no_audio sink;
68 		struct usb_ss_ep_comp_descriptor sink_comp;
69 		struct usb_endpoint_descriptor_no_audio source;
70 		struct usb_ss_ep_comp_descriptor source_comp;
71 	} __attribute__ ((__packed__)) ss_descs;
72 	struct usb_os_desc_header os_header;
73 	struct usb_ext_compat_desc os_desc;
74 
75 } __attribute__ ((__packed__)) descriptors = {
76 	.header = {
77 		.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
78 		.flags = htole32(FUNCTIONFS_HAS_FS_DESC |
79 				 FUNCTIONFS_HAS_HS_DESC |
80 				 FUNCTIONFS_HAS_SS_DESC |
81 				 FUNCTIONFS_HAS_MS_OS_DESC),
82 		.length = htole32(sizeof(descriptors)),
83 	},
84 	.fs_count = htole32(3),
85 	.fs_descs = {
86 		.intf = {
87 			.bLength = sizeof(descriptors.fs_descs.intf),
88 			.bDescriptorType = USB_DT_INTERFACE,
89 			.bNumEndpoints = 2,
90 			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
91 			.iInterface = 1,
92 		},
93 		.bulk_sink = {
94 			.bLength = sizeof(descriptors.fs_descs.bulk_sink),
95 			.bDescriptorType = USB_DT_ENDPOINT,
96 			.bEndpointAddress = 1 | USB_DIR_IN,
97 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
98 		},
99 		.bulk_source = {
100 			.bLength = sizeof(descriptors.fs_descs.bulk_source),
101 			.bDescriptorType = USB_DT_ENDPOINT,
102 			.bEndpointAddress = 2 | USB_DIR_OUT,
103 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
104 		},
105 	},
106 	.hs_count = htole32(3),
107 	.hs_descs = {
108 		.intf = {
109 			.bLength = sizeof(descriptors.hs_descs.intf),
110 			.bDescriptorType = USB_DT_INTERFACE,
111 			.bNumEndpoints = 2,
112 			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
113 			.iInterface = 1,
114 		},
115 		.bulk_sink = {
116 			.bLength = sizeof(descriptors.hs_descs.bulk_sink),
117 			.bDescriptorType = USB_DT_ENDPOINT,
118 			.bEndpointAddress = 1 | USB_DIR_IN,
119 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
120 			.wMaxPacketSize = htole16(512),
121 		},
122 		.bulk_source = {
123 			.bLength = sizeof(descriptors.hs_descs.bulk_source),
124 			.bDescriptorType = USB_DT_ENDPOINT,
125 			.bEndpointAddress = 2 | USB_DIR_OUT,
126 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
127 			.wMaxPacketSize = htole16(512),
128 		},
129 	},
130 	.ss_count = htole32(5),
131 	.ss_descs = {
132 		.intf = {
133 			.bLength = sizeof(descriptors.ss_descs.intf),
134 			.bDescriptorType = USB_DT_INTERFACE,
135 			.bInterfaceNumber = 0,
136 			.bNumEndpoints = 2,
137 			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
138 			.iInterface = 1,
139 		},
140 		.sink = {
141 			.bLength = sizeof(descriptors.ss_descs.sink),
142 			.bDescriptorType = USB_DT_ENDPOINT,
143 			.bEndpointAddress = 1 | USB_DIR_IN,
144 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
145 			.wMaxPacketSize = htole16(1024),
146 		},
147 		.sink_comp = {
148 			.bLength = sizeof(descriptors.ss_descs.sink_comp),
149 			.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
150 			.bMaxBurst = 4,
151 		},
152 		.source = {
153 			.bLength = sizeof(descriptors.ss_descs.source),
154 			.bDescriptorType = USB_DT_ENDPOINT,
155 			.bEndpointAddress = 2 | USB_DIR_OUT,
156 			.bmAttributes = USB_ENDPOINT_XFER_BULK,
157 			.wMaxPacketSize = htole16(1024),
158 		},
159 		.source_comp = {
160 			.bLength = sizeof(descriptors.ss_descs.source_comp),
161 			.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
162 			.bMaxBurst = 4,
163 		},
164 	},
165 	.os_count = htole32(1),
166 	.os_header = {
167 		.interface = htole32(1),
168 		.dwLength = htole32(sizeof(descriptors.os_header) +
169 			    sizeof(descriptors.os_desc)),
170 		.bcdVersion = htole32(1),
171 		.wIndex = htole32(4),
172 		.bCount = htole32(1),
173 		.Reserved = htole32(0),
174 	},
175 	.os_desc = {
176 		.bFirstInterfaceNumber = 0,
177 		.Reserved1 = htole32(1),
178 		.CompatibleID = {0},
179 		.SubCompatibleID = {0},
180 		.Reserved2 = {0},
181 	},
182 };
183 
184 #define STR_INTERFACE "AIO Test"
185 
186 static const struct {
187 	struct usb_functionfs_strings_head header;
188 	struct {
189 		__le16 code;
190 		const char str1[sizeof(STR_INTERFACE)];
191 	} __attribute__ ((__packed__)) lang0;
192 } __attribute__ ((__packed__)) strings = {
193 	.header = {
194 		.magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
195 		.length = htole32(sizeof(strings)),
196 		.str_count = htole32(1),
197 		.lang_count = htole32(1),
198 	},
199 	.lang0 = {
200 		htole16(0x0409), /* en-us */
201 		STR_INTERFACE,
202 	},
203 };
204 
205 /******************** Endpoints handling *******************************/
206 
display_event(struct usb_functionfs_event * event)207 static void display_event(struct usb_functionfs_event *event)
208 {
209 	static const char *const names[] = {
210 		[FUNCTIONFS_BIND] = "BIND",
211 		[FUNCTIONFS_UNBIND] = "UNBIND",
212 		[FUNCTIONFS_ENABLE] = "ENABLE",
213 		[FUNCTIONFS_DISABLE] = "DISABLE",
214 		[FUNCTIONFS_SETUP] = "SETUP",
215 		[FUNCTIONFS_SUSPEND] = "SUSPEND",
216 		[FUNCTIONFS_RESUME] = "RESUME",
217 	};
218 	switch (event->type) {
219 	case FUNCTIONFS_BIND:
220 	case FUNCTIONFS_UNBIND:
221 	case FUNCTIONFS_ENABLE:
222 	case FUNCTIONFS_DISABLE:
223 	case FUNCTIONFS_SETUP:
224 	case FUNCTIONFS_SUSPEND:
225 	case FUNCTIONFS_RESUME:
226 		printf("Event %s\n", names[event->type]);
227 	}
228 }
229 
handle_ep0(int ep0,bool * ready)230 static void handle_ep0(int ep0, bool *ready)
231 {
232 	struct usb_functionfs_event event;
233 	int ret;
234 
235 	struct pollfd pfds[1];
236 	pfds[0].fd = ep0;
237 	pfds[0].events = POLLIN;
238 
239 	ret = poll(pfds, 1, 0);
240 
241 	if (ret && (pfds[0].revents & POLLIN)) {
242 		ret = read(ep0, &event, sizeof(event));
243 		if (!ret) {
244 			perror("unable to read event from ep0");
245 			return;
246 		}
247 		display_event(&event);
248 		switch (event.type) {
249 		case FUNCTIONFS_SETUP:
250 			if (event.u.setup.bRequestType & USB_DIR_IN)
251 				write(ep0, NULL, 0);
252 			else
253 				read(ep0, NULL, 0);
254 			break;
255 
256 		case FUNCTIONFS_ENABLE:
257 			*ready = true;
258 			break;
259 
260 		case FUNCTIONFS_DISABLE:
261 			*ready = false;
262 			break;
263 
264 		default:
265 			break;
266 		}
267 	}
268 }
269 
main(int argc,char * argv[])270 int main(int argc, char *argv[])
271 {
272 	int i, ret;
273 	char *ep_path;
274 
275 	int ep0;
276 	int ep[2];
277 
278 	io_context_t ctx;
279 
280 	int evfd;
281 	fd_set rfds;
282 
283 	char *buf_in, *buf_out;
284 	struct iocb *iocb_in, *iocb_out;
285 	int req_in = 0, req_out = 0;
286 	bool ready;
287 
288 	if (argc != 2) {
289 		printf("ffs directory not specified!\n");
290 		return 1;
291 	}
292 
293 	ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
294 	if (!ep_path) {
295 		perror("malloc");
296 		return 1;
297 	}
298 
299 	/* open endpoint files */
300 	sprintf(ep_path, "%s/ep0", argv[1]);
301 	ep0 = open(ep_path, O_RDWR);
302 	if (ep0 < 0) {
303 		perror("unable to open ep0");
304 		return 1;
305 	}
306 	if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
307 		perror("unable do write descriptors");
308 		return 1;
309 	}
310 	if (write(ep0, &strings, sizeof(strings)) < 0) {
311 		perror("unable to write strings");
312 		return 1;
313 	}
314 	for (i = 0; i < 2; ++i) {
315 		sprintf(ep_path, "%s/ep%d", argv[1], i+1);
316 		ep[i] = open(ep_path, O_RDWR);
317 		if (ep[i] < 0) {
318 			printf("unable to open ep%d: %s\n", i+1,
319 			       strerror(errno));
320 			return 1;
321 		}
322 	}
323 
324 	free(ep_path);
325 
326 	memset(&ctx, 0, sizeof(ctx));
327 	/* setup aio context to handle up to 2 requests */
328 	if (io_setup(2, &ctx) < 0) {
329 		perror("unable to setup aio");
330 		return 1;
331 	}
332 
333 	evfd = eventfd(0, 0);
334 	if (evfd < 0) {
335 		perror("unable to open eventfd");
336 		return 1;
337 	}
338 
339 	/* alloc buffers and requests */
340 	buf_in = malloc(BUF_LEN);
341 	buf_out = malloc(BUF_LEN);
342 	iocb_in = malloc(sizeof(*iocb_in));
343 	iocb_out = malloc(sizeof(*iocb_out));
344 
345 	while (1) {
346 		FD_ZERO(&rfds);
347 		FD_SET(ep0, &rfds);
348 		FD_SET(evfd, &rfds);
349 
350 		ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
351 			     &rfds, NULL, NULL, NULL);
352 		if (ret < 0) {
353 			if (errno == EINTR)
354 				continue;
355 			perror("select");
356 			break;
357 		}
358 
359 		if (FD_ISSET(ep0, &rfds))
360 			handle_ep0(ep0, &ready);
361 
362 		/* we are waiting for function ENABLE */
363 		if (!ready)
364 			continue;
365 
366 		/* if something was submitted we wait for event */
367 		if (FD_ISSET(evfd, &rfds)) {
368 			uint64_t ev_cnt;
369 			ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
370 			if (ret < 0) {
371 				perror("unable to read eventfd");
372 				break;
373 			}
374 
375 			struct io_event e[2];
376 			/* we wait for one event */
377 			ret = io_getevents(ctx, 1, 2, e, NULL);
378 			/* if we got event */
379 			for (i = 0; i < ret; ++i) {
380 				if (e[i].obj->aio_fildes == ep[0]) {
381 					printf("ev=in; ret=%lu\n", e[i].res);
382 					req_in = 0;
383 				} else if (e[i].obj->aio_fildes == ep[1]) {
384 					printf("ev=out; ret=%lu\n", e[i].res);
385 					req_out = 0;
386 				}
387 			}
388 		}
389 
390 		if (!req_in) { /* if IN transfer not requested*/
391 			/* prepare write request */
392 			io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
393 			/* enable eventfd notification */
394 			iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
395 			iocb_in->u.c.resfd = evfd;
396 			/* submit table of requests */
397 			ret = io_submit(ctx, 1, &iocb_in);
398 			if (ret >= 0) { /* if ret > 0 request is queued */
399 				req_in = 1;
400 				printf("submit: in\n");
401 			} else
402 				perror("unable to submit request");
403 		}
404 		if (!req_out) { /* if OUT transfer not requested */
405 			/* prepare read request */
406 			io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
407 			/* enable eventfs notification */
408 			iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
409 			iocb_out->u.c.resfd = evfd;
410 			/* submit table of requests */
411 			ret = io_submit(ctx, 1, &iocb_out);
412 			if (ret >= 0) { /* if ret > 0 request is queued */
413 				req_out = 1;
414 				printf("submit: out\n");
415 			} else
416 				perror("unable to submit request");
417 		}
418 	}
419 
420 	/* free resources */
421 
422 	io_destroy(ctx);
423 
424 	free(buf_in);
425 	free(buf_out);
426 	free(iocb_in);
427 	free(iocb_out);
428 
429 	for (i = 0; i < 2; ++i)
430 		close(ep[i]);
431 	close(ep0);
432 
433 	return 0;
434 }
435