diff options
Diffstat (limited to 'tools/usb')
-rw-r--r-- | tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c | 349 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/multibuff/host_app/Makefile | 13 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/multibuff/host_app/test.c | 146 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/simple/device_app/aio_simple.c | 335 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/simple/host_app/Makefile | 13 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/simple/host_app/test.c | 148 |
6 files changed, 1004 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 | |||
29 | static 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 | |||
91 | static 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 | |||
112 | struct 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 | |||
122 | static 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 | |||
145 | static 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 | |||
177 | void 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 | |||
192 | void 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 | |||
203 | int 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 @@ | |||
1 | CC = gcc | ||
2 | LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0) | ||
3 | LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0) | ||
4 | WARNINGS = -Wall -Wextra | ||
5 | CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS) | ||
6 | LDFLAGS = $(LIBUSB_LIBS) | ||
7 | |||
8 | all: test | ||
9 | %: %.c | ||
10 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) | ||
11 | |||
12 | clean: | ||
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 | |||
26 | struct 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 | |||
37 | int 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 | |||
104 | error4: | ||
105 | if (state->attached == 1) | ||
106 | libusb_attach_kernel_driver(state->handle, 0); | ||
107 | |||
108 | error3: | ||
109 | libusb_close(state->handle); | ||
110 | |||
111 | error2: | ||
112 | libusb_free_device_list(list, 1); | ||
113 | |||
114 | error1: | ||
115 | libusb_exit(state->ctx); | ||
116 | return 1; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * test_exit - cleanup test program | ||
121 | */ | ||
122 | |||
123 | void 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 | |||
132 | int 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 | } | ||
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 | |||
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 | } | ||
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 @@ | |||
1 | CC = gcc | ||
2 | LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0) | ||
3 | LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0) | ||
4 | WARNINGS = -Wall -Wextra | ||
5 | CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS) | ||
6 | LDFLAGS = $(LIBUSB_LIBS) | ||
7 | |||
8 | all: test | ||
9 | %: %.c | ||
10 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) | ||
11 | |||
12 | clean: | ||
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 | |||
26 | struct 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 | |||
37 | int 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 | |||
104 | error4: | ||
105 | if (state->attached == 1) | ||
106 | libusb_attach_kernel_driver(state->handle, 0); | ||
107 | |||
108 | error3: | ||
109 | libusb_close(state->handle); | ||
110 | |||
111 | error2: | ||
112 | libusb_free_device_list(list, 1); | ||
113 | |||
114 | error1: | ||
115 | libusb_exit(state->ctx); | ||
116 | return 1; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * test_exit - cleanup test program | ||
121 | */ | ||
122 | |||
123 | void 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 | |||
132 | int 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 | } | ||