aboutsummaryrefslogtreecommitdiffstats
path: root/tools/usb/ffs-aio-example/simple
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/simple
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/simple')
-rw-r--r--tools/usb/ffs-aio-example/simple/device_app/aio_simple.c335
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/Makefile13
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/test.c148
3 files changed, 496 insertions, 0 deletions
diff --git a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
new file mode 100644
index 000000000000..f558664a3317
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
@@ -0,0 +1,335 @@
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
27static 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
87static 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
108static 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
131static 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
171int 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}
diff --git a/tools/usb/ffs-aio-example/simple/host_app/Makefile b/tools/usb/ffs-aio-example/simple/host_app/Makefile
new file mode 100644
index 000000000000..8c4a6f0aa82d
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/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/simple/host_app/test.c b/tools/usb/ffs-aio-example/simple/host_app/test.c
new file mode 100644
index 000000000000..64b6a57d8ca3
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/host_app/test.c
@@ -0,0 +1,148 @@
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 libusb_bulk_transfer(state.handle, EP_BULK_OUT, buffer, BUF_LEN,
145 &bytes, 500);
146 }
147 test_exit(&state);
148}