xref: /openbmc/linux/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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  
72  static 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  
138  static 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  
display_event(struct usb_functionfs_event * event)159  static 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  
handle_ep0(int ep0,bool * ready)182  static 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  
main(int argc,char * argv[])222  int 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