xref: /linux/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c (revision c0c914eca7f251c70facc37dfebeaf176601918d)
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 	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 
120 static 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 
141 static 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 
164 static 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 
204 int 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