aboutsummaryrefslogtreecommitdiffstats
path: root/tools/usb/ffs-aio-example/multibuff
diff options
context:
space:
mode:
authorRobert Baldyga <r.baldyga@samsung.com>2014-02-11 05:43:03 -0500
committerFelipe Balbi <balbi@ti.com>2014-05-14 10:23:24 -0400
commitb34e08d5c7062c19a3f582d23d5f649c79ff3409 (patch)
tree4d7674db849724848d2b4a7731b518dd8c5e4bb6 /tools/usb/ffs-aio-example/multibuff
parent60b6dbeffb8c253d1f80527b28611e5e236dec51 (diff)
tools: usb: aio example applications
This patch adds two example applications showing usage of Asynchronous I/O API of FunctionFS. First one (aio_simple) is simple example of bidirectional data transfer. Second one (aio_multibuff) shows multi-buffer data transfer, which may to be used in high performance applications. Both examples contains userspace applications for device and for host. It needs libaio library on the device, and libusb library on host. Signed-off-by: Robert Baldyga <r.baldyga@samsung.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'tools/usb/ffs-aio-example/multibuff')
-rw-r--r--tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c349
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/Makefile13
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/test.c146
3 files changed, 508 insertions, 0 deletions
diff --git a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
new file mode 100644
index 000000000000..87216a0c4a8b
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
@@ -0,0 +1,349 @@
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#define BUFS_MAX 128
25#define AIO_MAX (BUFS_MAX*2)
26
27/******************** Descriptors and Strings *******************************/
28
29static const struct {
30 struct usb_functionfs_descs_head header;
31 struct {
32 struct usb_interface_descriptor intf;
33 struct usb_endpoint_descriptor_no_audio bulk_sink;
34 struct usb_endpoint_descriptor_no_audio bulk_source;
35 } __attribute__ ((__packed__)) fs_descs, hs_descs;
36} __attribute__ ((__packed__)) descriptors = {
37 .header = {
38 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
39 .length = htole32(sizeof(descriptors)),
40 .fs_count = 3,
41 .hs_count = 3,
42 },
43 .fs_descs = {
44 .intf = {
45 .bLength = sizeof(descriptors.fs_descs.intf),
46 .bDescriptorType = USB_DT_INTERFACE,
47 .bNumEndpoints = 2,
48 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
49 .iInterface = 1,
50 },
51 .bulk_sink = {
52 .bLength = sizeof(descriptors.fs_descs.bulk_sink),
53 .bDescriptorType = USB_DT_ENDPOINT,
54 .bEndpointAddress = 1 | USB_DIR_IN,
55 .bmAttributes = USB_ENDPOINT_XFER_BULK,
56 },
57 .bulk_source = {
58 .bLength = sizeof(descriptors.fs_descs.bulk_source),
59 .bDescriptorType = USB_DT_ENDPOINT,
60 .bEndpointAddress = 2 | USB_DIR_OUT,
61 .bmAttributes = USB_ENDPOINT_XFER_BULK,
62 },
63 },
64 .hs_descs = {
65 .intf = {
66 .bLength = sizeof(descriptors.hs_descs.intf),
67 .bDescriptorType = USB_DT_INTERFACE,
68 .bNumEndpoints = 2,
69 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
70 .iInterface = 1,
71 },
72 .bulk_sink = {
73 .bLength = sizeof(descriptors.hs_descs.bulk_sink),
74 .bDescriptorType = USB_DT_ENDPOINT,
75 .bEndpointAddress = 1 | USB_DIR_IN,
76 .bmAttributes = USB_ENDPOINT_XFER_BULK,
77 .wMaxPacketSize = htole16(512),
78 },
79 .bulk_source = {
80 .bLength = sizeof(descriptors.hs_descs.bulk_source),
81 .bDescriptorType = USB_DT_ENDPOINT,
82 .bEndpointAddress = 2 | USB_DIR_OUT,
83 .bmAttributes = USB_ENDPOINT_XFER_BULK,
84 .wMaxPacketSize = htole16(512),
85 },
86 },
87};
88
89#define STR_INTERFACE "AIO Test"
90
91static const struct {
92 struct usb_functionfs_strings_head header;
93 struct {
94 __le16 code;
95 const char str1[sizeof(STR_INTERFACE)];
96 } __attribute__ ((__packed__)) lang0;
97} __attribute__ ((__packed__)) strings = {
98 .header = {
99 .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
100 .length = htole32(sizeof(strings)),
101 .str_count = htole32(1),
102 .lang_count = htole32(1),
103 },
104 .lang0 = {
105 htole16(0x0409), /* en-us */
106 STR_INTERFACE,
107 },
108};
109
110/********************** Buffer structure *******************************/
111
112struct io_buffer {
113 struct iocb **iocb;
114 unsigned char **buf;
115 unsigned cnt;
116 unsigned len;
117 unsigned requested;
118};
119
120/******************** Endpoints handling *******************************/
121
122static void display_event(struct usb_functionfs_event *event)
123{
124 static const char *const names[] = {
125 [FUNCTIONFS_BIND] = "BIND",
126 [FUNCTIONFS_UNBIND] = "UNBIND",
127 [FUNCTIONFS_ENABLE] = "ENABLE",
128 [FUNCTIONFS_DISABLE] = "DISABLE",
129 [FUNCTIONFS_SETUP] = "SETUP",
130 [FUNCTIONFS_SUSPEND] = "SUSPEND",
131 [FUNCTIONFS_RESUME] = "RESUME",
132 };
133 switch (event->type) {
134 case FUNCTIONFS_BIND:
135 case FUNCTIONFS_UNBIND:
136 case FUNCTIONFS_ENABLE:
137 case FUNCTIONFS_DISABLE:
138 case FUNCTIONFS_SETUP:
139 case FUNCTIONFS_SUSPEND:
140 case FUNCTIONFS_RESUME:
141 printf("Event %s\n", names[event->type]);
142 }
143}
144
145static void handle_ep0(int ep0, bool *ready)
146{
147 int ret;
148 struct usb_functionfs_event event;
149
150 ret = read(ep0, &event, sizeof(event));
151 if (!ret) {
152 perror("unable to read event from ep0");
153 return;
154 }
155 display_event(&event);
156 switch (event.type) {
157 case FUNCTIONFS_SETUP:
158 if (event.u.setup.bRequestType & USB_DIR_IN)
159 write(ep0, NULL, 0);
160 else
161 read(ep0, NULL, 0);
162 break;
163
164 case FUNCTIONFS_ENABLE:
165 *ready = true;
166 break;
167
168 case FUNCTIONFS_DISABLE:
169 *ready = false;
170 break;
171
172 default:
173 break;
174 }
175}
176
177void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)
178{
179 unsigned i;
180 iobuf->buf = malloc(n*sizeof(*iobuf->buf));
181 iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
182 iobuf->cnt = n;
183 iobuf->len = len;
184 iobuf->requested = 0;
185 for (i = 0; i < n; ++i) {
186 iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
187 iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
188 }
189 iobuf->cnt = n;
190}
191
192void delete_bufs(struct io_buffer *iobuf)
193{
194 unsigned i;
195 for (i = 0; i < iobuf->cnt; ++i) {
196 free(iobuf->buf[i]);
197 free(iobuf->iocb[i]);
198 }
199 free(iobuf->buf);
200 free(iobuf->iocb);
201}
202
203int main(int argc, char *argv[])
204{
205 int ret;
206 unsigned i, j;
207 char *ep_path;
208
209 int ep0, ep1;
210
211 io_context_t ctx;
212
213 int evfd;
214 fd_set rfds;
215
216 struct io_buffer iobuf[2];
217 int actual = 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 sprintf(ep_path, "%s/ep1", argv[1]);
247 ep1 = open(ep_path, O_RDWR);
248 if (ep1 < 0) {
249 perror("unable to open ep1");
250 return 1;
251 }
252
253 free(ep_path);
254
255 memset(&ctx, 0, sizeof(ctx));
256 /* setup aio context to handle up to AIO_MAX requests */
257 if (io_setup(AIO_MAX, &ctx) < 0) {
258 perror("unable to setup aio");
259 return 1;
260 }
261
262 evfd = eventfd(0, 0);
263 if (evfd < 0) {
264 perror("unable to open eventfd");
265 return 1;
266 }
267
268 for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
269 init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);
270
271 while (1) {
272 FD_ZERO(&rfds);
273 FD_SET(ep0, &rfds);
274 FD_SET(evfd, &rfds);
275
276 ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
277 &rfds, NULL, NULL, NULL);
278 if (ret < 0) {
279 if (errno == EINTR)
280 continue;
281 perror("select");
282 break;
283 }
284
285 if (FD_ISSET(ep0, &rfds))
286 handle_ep0(ep0, &ready);
287
288 /* we are waiting for function ENABLE */
289 if (!ready)
290 continue;
291
292 /*
293 * when we're preparing new data to submit,
294 * second buffer being transmitted
295 */
296 for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {
297 if (iobuf[i].requested)
298 continue;
299 /* prepare requests */
300 for (j = 0; j < iobuf[i].cnt; ++j) {
301 io_prep_pwrite(iobuf[i].iocb[j], ep1,
302 iobuf[i].buf[j],
303 iobuf[i].len, 0);
304 /* enable eventfd notification */
305 iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;
306 iobuf[i].iocb[j]->u.c.resfd = evfd;
307 }
308 /* submit table of requests */
309 ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);
310 if (ret >= 0) {
311 iobuf[i].requested = ret;
312 printf("submit: %d requests buf: %d\n", ret, i);
313 } else
314 perror("unable to submit reqests");
315 }
316
317 /* if event is ready to read */
318 if (!FD_ISSET(evfd, &rfds))
319 continue;
320
321 uint64_t ev_cnt;
322 ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
323 if (ret < 0) {
324 perror("unable to read eventfd");
325 break;
326 }
327
328 struct io_event e[BUFS_MAX];
329 /* we read aio events */
330 ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);
331 if (ret > 0) /* if we got events */
332 iobuf[actual].requested -= ret;
333
334 /* if all req's from iocb completed */
335 if (!iobuf[actual].requested)
336 actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));
337 }
338
339 /* free resources */
340
341 for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
342 delete_bufs(&iobuf[i]);
343 io_destroy(ctx);
344
345 close(ep1);
346 close(ep0);
347
348 return 0;
349}
diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/Makefile b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile
new file mode 100644
index 000000000000..8c4a6f0aa82d
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile
@@ -0,0 +1,13 @@
1CC = gcc
2LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0)
3LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0)
4WARNINGS = -Wall -Wextra
5CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS)
6LDFLAGS = $(LIBUSB_LIBS)
7
8all: test
9%: %.c
10 $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
11
12clean:
13 $(RM) test
diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/test.c b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
new file mode 100644
index 000000000000..b0ad8747d03f
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
@@ -0,0 +1,146 @@
1#include <libusb.h>
2#include <stdio.h>
3#include <string.h>
4#include <unistd.h>
5
6#define VENDOR 0x1d6b
7#define PRODUCT 0x0105
8
9/* endpoints indexes */
10
11#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN)
12#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT)
13
14#define BUF_LEN 8192
15
16/*
17 * struct test_state - describes test program state
18 * @list: list of devices returned by libusb_get_device_list function
19 * @found: pointer to struct describing tested device
20 * @ctx: context, set to NULL
21 * @handle: handle of tested device
22 * @attached: indicates that device was attached to kernel, and has to be
23 * reattached at the end of test program
24 */
25
26struct test_state {
27 libusb_device *found;
28 libusb_context *ctx;
29 libusb_device_handle *handle;
30 int attached;
31};
32
33/*
34 * test_init - initialize test program
35 */
36
37int test_init(struct test_state *state)
38{
39 int i, ret;
40 ssize_t cnt;
41 libusb_device **list;
42
43 state->found = NULL;
44 state->ctx = NULL;
45 state->handle = NULL;
46 state->attached = 0;
47
48 ret = libusb_init(&state->ctx);
49 if (ret) {
50 printf("cannot init libusb: %s\n", libusb_error_name(ret));
51 return 1;
52 }
53
54 cnt = libusb_get_device_list(state->ctx, &list);
55 if (cnt <= 0) {
56 printf("no devices found\n");
57 goto error1;
58 }
59
60 for (i = 0; i < cnt; ++i) {
61 libusb_device *dev = list[i];
62 struct libusb_device_descriptor desc;
63 ret = libusb_get_device_descriptor(dev, &desc);
64 if (ret) {
65 printf("unable to get device descriptor: %s\n",
66 libusb_error_name(ret));
67 goto error2;
68 }
69 if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) {
70 state->found = dev;
71 break;
72 }
73 }
74
75 if (!state->found) {
76 printf("no devices found\n");
77 goto error2;
78 }
79
80 ret = libusb_open(state->found, &state->handle);
81 if (ret) {
82 printf("cannot open device: %s\n", libusb_error_name(ret));
83 goto error2;
84 }
85
86 if (libusb_claim_interface(state->handle, 0)) {
87 ret = libusb_detach_kernel_driver(state->handle, 0);
88 if (ret) {
89 printf("unable to detach kernel driver: %s\n",
90 libusb_error_name(ret));
91 goto error3;
92 }
93 state->attached = 1;
94 ret = libusb_claim_interface(state->handle, 0);
95 if (ret) {
96 printf("cannot claim interface: %s\n",
97 libusb_error_name(ret));
98 goto error4;
99 }
100 }
101
102 return 0;
103
104error4:
105 if (state->attached == 1)
106 libusb_attach_kernel_driver(state->handle, 0);
107
108error3:
109 libusb_close(state->handle);
110
111error2:
112 libusb_free_device_list(list, 1);
113
114error1:
115 libusb_exit(state->ctx);
116 return 1;
117}
118
119/*
120 * test_exit - cleanup test program
121 */
122
123void test_exit(struct test_state *state)
124{
125 libusb_release_interface(state->handle, 0);
126 if (state->attached == 1)
127 libusb_attach_kernel_driver(state->handle, 0);
128 libusb_close(state->handle);
129 libusb_exit(state->ctx);
130}
131
132int main(void)
133{
134 struct test_state state;
135
136 if (test_init(&state))
137 return 1;
138
139 while (1) {
140 static unsigned char buffer[BUF_LEN];
141 int bytes;
142 libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN,
143 &bytes, 500);
144 }
145 test_exit(&state);
146}