tools: ffs-aio-example: add license information
[deliverable/linux.git] / tools / usb / ffs-aio-example / simple / device_app / aio_simple.c
CommitLineData
aa491320
RB
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
b34e08d5
RB
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
54static const struct {
0ebe9910
RB
55 struct usb_functionfs_descs_head_v2 header;
56 __le32 fs_count;
57 __le32 hs_count;
b34e08d5
RB
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 = {
0ebe9910
RB
65 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
66 .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
67 FUNCTIONFS_HAS_HS_DESC),
b34e08d5 68 .length = htole32(sizeof(descriptors)),
b34e08d5 69 },
0ebe9910 70 .fs_count = htole32(3),
b34e08d5
RB
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 },
0ebe9910 92 .hs_count = htole32(3),
b34e08d5
RB
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 },
107 .bulk_source = {
108 .bLength = sizeof(descriptors.hs_descs.bulk_source),
109 .bDescriptorType = USB_DT_ENDPOINT,
110 .bEndpointAddress = 2 | USB_DIR_OUT,
111 .bmAttributes = USB_ENDPOINT_XFER_BULK,
112 },
113 },
114};
115
116#define STR_INTERFACE "AIO Test"
117
118static const struct {
119 struct usb_functionfs_strings_head header;
120 struct {
121 __le16 code;
122 const char str1[sizeof(STR_INTERFACE)];
123 } __attribute__ ((__packed__)) lang0;
124} __attribute__ ((__packed__)) strings = {
125 .header = {
126 .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
127 .length = htole32(sizeof(strings)),
128 .str_count = htole32(1),
129 .lang_count = htole32(1),
130 },
131 .lang0 = {
132 htole16(0x0409), /* en-us */
133 STR_INTERFACE,
134 },
135};
136
137/******************** Endpoints handling *******************************/
138
139static void display_event(struct usb_functionfs_event *event)
140{
141 static const char *const names[] = {
142 [FUNCTIONFS_BIND] = "BIND",
143 [FUNCTIONFS_UNBIND] = "UNBIND",
144 [FUNCTIONFS_ENABLE] = "ENABLE",
145 [FUNCTIONFS_DISABLE] = "DISABLE",
146 [FUNCTIONFS_SETUP] = "SETUP",
147 [FUNCTIONFS_SUSPEND] = "SUSPEND",
148 [FUNCTIONFS_RESUME] = "RESUME",
149 };
150 switch (event->type) {
151 case FUNCTIONFS_BIND:
152 case FUNCTIONFS_UNBIND:
153 case FUNCTIONFS_ENABLE:
154 case FUNCTIONFS_DISABLE:
155 case FUNCTIONFS_SETUP:
156 case FUNCTIONFS_SUSPEND:
157 case FUNCTIONFS_RESUME:
158 printf("Event %s\n", names[event->type]);
159 }
160}
161
162static void handle_ep0(int ep0, bool *ready)
163{
164 struct usb_functionfs_event event;
165 int ret;
166
167 struct pollfd pfds[1];
168 pfds[0].fd = ep0;
169 pfds[0].events = POLLIN;
170
171 ret = poll(pfds, 1, 0);
172
173 if (ret && (pfds[0].revents & POLLIN)) {
174 ret = read(ep0, &event, sizeof(event));
175 if (!ret) {
176 perror("unable to read event from ep0");
177 return;
178 }
179 display_event(&event);
180 switch (event.type) {
181 case FUNCTIONFS_SETUP:
182 if (event.u.setup.bRequestType & USB_DIR_IN)
183 write(ep0, NULL, 0);
184 else
185 read(ep0, NULL, 0);
186 break;
187
188 case FUNCTIONFS_ENABLE:
189 *ready = true;
190 break;
191
192 case FUNCTIONFS_DISABLE:
193 *ready = false;
194 break;
195
196 default:
197 break;
198 }
199 }
200}
201
202int main(int argc, char *argv[])
203{
204 int i, ret;
205 char *ep_path;
206
207 int ep0;
208 int ep[2];
209
210 io_context_t ctx;
211
212 int evfd;
213 fd_set rfds;
214
215 char *buf_in, *buf_out;
216 struct iocb *iocb_in, *iocb_out;
217 int req_in = 0, req_out = 0;
218 bool ready;
219
220 if (argc != 2) {
221 printf("ffs directory not specified!\n");
222 return 1;
223 }
224
225 ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
226 if (!ep_path) {
227 perror("malloc");
228 return 1;
229 }
230
231 /* open endpoint files */
232 sprintf(ep_path, "%s/ep0", argv[1]);
233 ep0 = open(ep_path, O_RDWR);
234 if (ep0 < 0) {
235 perror("unable to open ep0");
236 return 1;
237 }
238 if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
239 perror("unable do write descriptors");
240 return 1;
241 }
242 if (write(ep0, &strings, sizeof(strings)) < 0) {
243 perror("unable to write strings");
244 return 1;
245 }
246 for (i = 0; i < 2; ++i) {
247 sprintf(ep_path, "%s/ep%d", argv[1], i+1);
248 ep[i] = open(ep_path, O_RDWR);
249 if (ep[i] < 0) {
250 printf("unable to open ep%d: %s\n", i+1,
251 strerror(errno));
252 return 1;
253 }
254 }
255
256 free(ep_path);
257
258 memset(&ctx, 0, sizeof(ctx));
259 /* setup aio context to handle up to 2 requests */
260 if (io_setup(2, &ctx) < 0) {
261 perror("unable to setup aio");
262 return 1;
263 }
264
265 evfd = eventfd(0, 0);
266 if (evfd < 0) {
267 perror("unable to open eventfd");
268 return 1;
269 }
270
271 /* alloc buffers and requests */
272 buf_in = malloc(BUF_LEN);
273 buf_out = malloc(BUF_LEN);
274 iocb_in = malloc(sizeof(*iocb_in));
275 iocb_out = malloc(sizeof(*iocb_out));
276
277 while (1) {
278 FD_ZERO(&rfds);
279 FD_SET(ep0, &rfds);
280 FD_SET(evfd, &rfds);
281
282 ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
283 &rfds, NULL, NULL, NULL);
284 if (ret < 0) {
285 if (errno == EINTR)
286 continue;
287 perror("select");
288 break;
289 }
290
291 if (FD_ISSET(ep0, &rfds))
292 handle_ep0(ep0, &ready);
293
294 /* we are waiting for function ENABLE */
295 if (!ready)
296 continue;
297
298 /* if something was submitted we wait for event */
299 if (FD_ISSET(evfd, &rfds)) {
300 uint64_t ev_cnt;
301 ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
302 if (ret < 0) {
303 perror("unable to read eventfd");
304 break;
305 }
306
307 struct io_event e[2];
308 /* we wait for one event */
309 ret = io_getevents(ctx, 1, 2, e, NULL);
310 /* if we got event */
311 for (i = 0; i < ret; ++i) {
312 if (e[i].obj->aio_fildes == ep[0]) {
313 printf("ev=in; ret=%lu\n", e[i].res);
314 req_in = 0;
315 } else if (e[i].obj->aio_fildes == ep[1]) {
316 printf("ev=out; ret=%lu\n", e[i].res);
317 req_out = 0;
318 }
319 }
320 }
321
322 if (!req_in) { /* if IN transfer not requested*/
323 /* prepare write request */
324 io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
325 /* enable eventfd notification */
326 iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
327 iocb_in->u.c.resfd = evfd;
328 /* submit table of requests */
329 ret = io_submit(ctx, 1, &iocb_in);
330 if (ret >= 0) { /* if ret > 0 request is queued */
331 req_in = 1;
332 printf("submit: in\n");
333 } else
334 perror("unable to submit request");
335 }
336 if (!req_out) { /* if OUT transfer not requested */
337 /* prepare read request */
338 io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
339 /* enable eventfs notification */
340 iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
341 iocb_out->u.c.resfd = evfd;
342 /* submit table of requests */
343 ret = io_submit(ctx, 1, &iocb_out);
344 if (ret >= 0) { /* if ret > 0 request is queued */
345 req_out = 1;
346 printf("submit: out\n");
347 } else
348 perror("unable to submit request");
349 }
350 }
351
352 /* free resources */
353
354 io_destroy(ctx);
355
356 free(buf_in);
357 free(buf_out);
358 free(iocb_in);
359 free(iocb_out);
360
361 for (i = 0; i < 2; ++i)
362 close(ep[i]);
363 close(ep0);
364
365 return 0;
366}
This page took 0.048131 seconds and 5 git commands to generate.