Linux-libre 3.16.85-gnu
[librecmc/linux-libre.git] / tools / usb / ffs-aio-example / simple / device_app / aio_simple.c
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 = 3,
39                 .hs_count = 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 }