Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
26292 views
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
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
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
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
388