Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.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
#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
#define BUFS_MAX 128
52
#define AIO_MAX (BUFS_MAX*2)
53
54
/******************** Descriptors and Strings *******************************/
55
56
static const struct {
57
struct usb_functionfs_descs_head_v2 header;
58
__le32 fs_count;
59
__le32 hs_count;
60
struct {
61
struct usb_interface_descriptor intf;
62
struct usb_endpoint_descriptor_no_audio bulk_sink;
63
struct usb_endpoint_descriptor_no_audio bulk_source;
64
} __attribute__ ((__packed__)) fs_descs, hs_descs;
65
} __attribute__ ((__packed__)) descriptors = {
66
.header = {
67
.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
68
.flags = htole32(FUNCTIONFS_HAS_FS_DESC |
69
FUNCTIONFS_HAS_HS_DESC),
70
.length = htole32(sizeof(descriptors)),
71
},
72
.fs_count = htole32(3),
73
.fs_descs = {
74
.intf = {
75
.bLength = sizeof(descriptors.fs_descs.intf),
76
.bDescriptorType = USB_DT_INTERFACE,
77
.bNumEndpoints = 2,
78
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
79
.iInterface = 1,
80
},
81
.bulk_sink = {
82
.bLength = sizeof(descriptors.fs_descs.bulk_sink),
83
.bDescriptorType = USB_DT_ENDPOINT,
84
.bEndpointAddress = 1 | USB_DIR_IN,
85
.bmAttributes = USB_ENDPOINT_XFER_BULK,
86
},
87
.bulk_source = {
88
.bLength = sizeof(descriptors.fs_descs.bulk_source),
89
.bDescriptorType = USB_DT_ENDPOINT,
90
.bEndpointAddress = 2 | USB_DIR_OUT,
91
.bmAttributes = USB_ENDPOINT_XFER_BULK,
92
},
93
},
94
.hs_count = htole32(3),
95
.hs_descs = {
96
.intf = {
97
.bLength = sizeof(descriptors.hs_descs.intf),
98
.bDescriptorType = USB_DT_INTERFACE,
99
.bNumEndpoints = 2,
100
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
101
.iInterface = 1,
102
},
103
.bulk_sink = {
104
.bLength = sizeof(descriptors.hs_descs.bulk_sink),
105
.bDescriptorType = USB_DT_ENDPOINT,
106
.bEndpointAddress = 1 | USB_DIR_IN,
107
.bmAttributes = USB_ENDPOINT_XFER_BULK,
108
.wMaxPacketSize = htole16(512),
109
},
110
.bulk_source = {
111
.bLength = sizeof(descriptors.hs_descs.bulk_source),
112
.bDescriptorType = USB_DT_ENDPOINT,
113
.bEndpointAddress = 2 | USB_DIR_OUT,
114
.bmAttributes = USB_ENDPOINT_XFER_BULK,
115
.wMaxPacketSize = htole16(512),
116
},
117
},
118
};
119
120
#define STR_INTERFACE "AIO Test"
121
122
static const struct {
123
struct usb_functionfs_strings_head header;
124
struct {
125
__le16 code;
126
const char str1[sizeof(STR_INTERFACE)];
127
} __attribute__ ((__packed__)) lang0;
128
} __attribute__ ((__packed__)) strings = {
129
.header = {
130
.magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
131
.length = htole32(sizeof(strings)),
132
.str_count = htole32(1),
133
.lang_count = htole32(1),
134
},
135
.lang0 = {
136
htole16(0x0409), /* en-us */
137
STR_INTERFACE,
138
},
139
};
140
141
/********************** Buffer structure *******************************/
142
143
struct io_buffer {
144
struct iocb **iocb;
145
unsigned char **buf;
146
unsigned cnt;
147
unsigned len;
148
unsigned requested;
149
};
150
151
/******************** Endpoints handling *******************************/
152
153
static void display_event(struct usb_functionfs_event *event)
154
{
155
static const char *const names[] = {
156
[FUNCTIONFS_BIND] = "BIND",
157
[FUNCTIONFS_UNBIND] = "UNBIND",
158
[FUNCTIONFS_ENABLE] = "ENABLE",
159
[FUNCTIONFS_DISABLE] = "DISABLE",
160
[FUNCTIONFS_SETUP] = "SETUP",
161
[FUNCTIONFS_SUSPEND] = "SUSPEND",
162
[FUNCTIONFS_RESUME] = "RESUME",
163
};
164
switch (event->type) {
165
case FUNCTIONFS_BIND:
166
case FUNCTIONFS_UNBIND:
167
case FUNCTIONFS_ENABLE:
168
case FUNCTIONFS_DISABLE:
169
case FUNCTIONFS_SETUP:
170
case FUNCTIONFS_SUSPEND:
171
case FUNCTIONFS_RESUME:
172
printf("Event %s\n", names[event->type]);
173
}
174
}
175
176
static void handle_ep0(int ep0, bool *ready)
177
{
178
int ret;
179
struct usb_functionfs_event event;
180
181
ret = read(ep0, &event, sizeof(event));
182
if (!ret) {
183
perror("unable to read event from ep0");
184
return;
185
}
186
display_event(&event);
187
switch (event.type) {
188
case FUNCTIONFS_SETUP:
189
if (event.u.setup.bRequestType & USB_DIR_IN)
190
write(ep0, NULL, 0);
191
else
192
read(ep0, NULL, 0);
193
break;
194
195
case FUNCTIONFS_ENABLE:
196
*ready = true;
197
break;
198
199
case FUNCTIONFS_DISABLE:
200
*ready = false;
201
break;
202
203
default:
204
break;
205
}
206
}
207
208
void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)
209
{
210
unsigned i;
211
iobuf->buf = malloc(n*sizeof(*iobuf->buf));
212
iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
213
iobuf->cnt = n;
214
iobuf->len = len;
215
iobuf->requested = 0;
216
for (i = 0; i < n; ++i) {
217
iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
218
iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
219
}
220
iobuf->cnt = n;
221
}
222
223
void delete_bufs(struct io_buffer *iobuf)
224
{
225
unsigned i;
226
for (i = 0; i < iobuf->cnt; ++i) {
227
free(iobuf->buf[i]);
228
free(iobuf->iocb[i]);
229
}
230
free(iobuf->buf);
231
free(iobuf->iocb);
232
}
233
234
int main(int argc, char *argv[])
235
{
236
int ret;
237
unsigned i, j;
238
char *ep_path;
239
240
int ep0, ep1;
241
242
io_context_t ctx;
243
244
int evfd;
245
fd_set rfds;
246
247
struct io_buffer iobuf[2];
248
int actual = 0;
249
bool ready;
250
251
if (argc != 2) {
252
printf("ffs directory not specified!\n");
253
return 1;
254
}
255
256
ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
257
if (!ep_path) {
258
perror("malloc");
259
return 1;
260
}
261
262
/* open endpoint files */
263
sprintf(ep_path, "%s/ep0", argv[1]);
264
ep0 = open(ep_path, O_RDWR);
265
if (ep0 < 0) {
266
perror("unable to open ep0");
267
return 1;
268
}
269
if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
270
perror("unable do write descriptors");
271
return 1;
272
}
273
if (write(ep0, &strings, sizeof(strings)) < 0) {
274
perror("unable to write strings");
275
return 1;
276
}
277
sprintf(ep_path, "%s/ep1", argv[1]);
278
ep1 = open(ep_path, O_RDWR);
279
if (ep1 < 0) {
280
perror("unable to open ep1");
281
return 1;
282
}
283
284
free(ep_path);
285
286
memset(&ctx, 0, sizeof(ctx));
287
/* setup aio context to handle up to AIO_MAX requests */
288
if (io_setup(AIO_MAX, &ctx) < 0) {
289
perror("unable to setup aio");
290
return 1;
291
}
292
293
evfd = eventfd(0, 0);
294
if (evfd < 0) {
295
perror("unable to open eventfd");
296
return 1;
297
}
298
299
for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
300
init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);
301
302
while (1) {
303
FD_ZERO(&rfds);
304
FD_SET(ep0, &rfds);
305
FD_SET(evfd, &rfds);
306
307
ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
308
&rfds, NULL, NULL, NULL);
309
if (ret < 0) {
310
if (errno == EINTR)
311
continue;
312
perror("select");
313
break;
314
}
315
316
if (FD_ISSET(ep0, &rfds))
317
handle_ep0(ep0, &ready);
318
319
/* we are waiting for function ENABLE */
320
if (!ready)
321
continue;
322
323
/*
324
* when we're preparing new data to submit,
325
* second buffer being transmitted
326
*/
327
for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {
328
if (iobuf[i].requested)
329
continue;
330
/* prepare requests */
331
for (j = 0; j < iobuf[i].cnt; ++j) {
332
io_prep_pwrite(iobuf[i].iocb[j], ep1,
333
iobuf[i].buf[j],
334
iobuf[i].len, 0);
335
/* enable eventfd notification */
336
iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;
337
iobuf[i].iocb[j]->u.c.resfd = evfd;
338
}
339
/* submit table of requests */
340
ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);
341
if (ret >= 0) {
342
iobuf[i].requested = ret;
343
printf("submit: %d requests buf: %d\n", ret, i);
344
} else
345
perror("unable to submit requests");
346
}
347
348
/* if event is ready to read */
349
if (!FD_ISSET(evfd, &rfds))
350
continue;
351
352
uint64_t ev_cnt;
353
ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
354
if (ret < 0) {
355
perror("unable to read eventfd");
356
break;
357
}
358
359
struct io_event e[BUFS_MAX];
360
/* we read aio events */
361
ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);
362
if (ret > 0) /* if we got events */
363
iobuf[actual].requested -= ret;
364
365
/* if all req's from iocb completed */
366
if (!iobuf[actual].requested)
367
actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));
368
}
369
370
/* free resources */
371
372
for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
373
delete_bufs(&iobuf[i]);
374
io_destroy(ctx);
375
376
close(ep1);
377
close(ep0);
378
379
return 0;
380
}
381
382