diff options
Diffstat (limited to 'tools/usb/ffs-aio-example')
-rw-r--r-- | tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c | 380 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/multibuff/host_app/Makefile | 14 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/multibuff/host_app/test.c | 175 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/simple/device_app/aio_simple.c | 386 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/simple/host_app/Makefile | 14 | ||||
-rw-r--r-- | tools/usb/ffs-aio-example/simple/host_app/test.c | 178 |
6 files changed, 1147 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 0000000000..aaca1f44e7 --- /dev/null +++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c @@ -0,0 +1,380 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + +#define _BSD_SOURCE /* for endian.h */ + +#include <endian.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/poll.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/eventfd.h> + +#include "libaio.h" +#define IOCB_FLAG_RESFD (1 << 0) + +#include <linux/usb/functionfs.h> + +#define BUF_LEN 8192 +#define BUFS_MAX 128 +#define AIO_MAX (BUFS_MAX*2) + +/******************** Descriptors and Strings *******************************/ + +static const struct { + struct usb_functionfs_descs_head_v2 header; + __le32 fs_count; + __le32 hs_count; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio bulk_sink; + struct usb_endpoint_descriptor_no_audio bulk_source; + } __attribute__ ((__packed__)) fs_descs, hs_descs; +} __attribute__ ((__packed__)) descriptors = { + .header = { + .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), + .flags = htole32(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC), + .length = htole32(sizeof(descriptors)), + }, + .fs_count = htole32(3), + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = 1, + }, + .bulk_sink = { + .bLength = sizeof(descriptors.fs_descs.bulk_sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .bulk_source = { + .bLength = sizeof(descriptors.fs_descs.bulk_source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + }, + .hs_count = htole32(3), + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = 1, + }, + .bulk_sink = { + .bLength = sizeof(descriptors.hs_descs.bulk_sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = htole16(512), + }, + .bulk_source = { + .bLength = sizeof(descriptors.hs_descs.bulk_source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = htole16(512), + }, + }, +}; + +#define STR_INTERFACE "AIO Test" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE)]; + } __attribute__ ((__packed__)) lang0; +} __attribute__ ((__packed__)) strings = { + .header = { + .magic = htole32(FUNCTIONFS_STRINGS_MAGIC), + .length = htole32(sizeof(strings)), + .str_count = htole32(1), + .lang_count = htole32(1), + }, + .lang0 = { + htole16(0x0409), /* en-us */ + STR_INTERFACE, + }, +}; + +/********************** Buffer structure *******************************/ + +struct io_buffer { + struct iocb **iocb; + unsigned char **buf; + unsigned cnt; + unsigned len; + unsigned requested; +}; + +/******************** Endpoints handling *******************************/ + +static void display_event(struct usb_functionfs_event *event) +{ + static const char *const names[] = { + [FUNCTIONFS_BIND] = "BIND", + [FUNCTIONFS_UNBIND] = "UNBIND", + [FUNCTIONFS_ENABLE] = "ENABLE", + [FUNCTIONFS_DISABLE] = "DISABLE", + [FUNCTIONFS_SETUP] = "SETUP", + [FUNCTIONFS_SUSPEND] = "SUSPEND", + [FUNCTIONFS_RESUME] = "RESUME", + }; + switch (event->type) { + case FUNCTIONFS_BIND: + case FUNCTIONFS_UNBIND: + case FUNCTIONFS_ENABLE: + case FUNCTIONFS_DISABLE: + case FUNCTIONFS_SETUP: + case FUNCTIONFS_SUSPEND: + case FUNCTIONFS_RESUME: + printf("Event %s\n", names[event->type]); + } +} + +static void handle_ep0(int ep0, bool *ready) +{ + int ret; + struct usb_functionfs_event event; + + ret = read(ep0, &event, sizeof(event)); + if (!ret) { + perror("unable to read event from ep0"); + return; + } + display_event(&event); + switch (event.type) { + case FUNCTIONFS_SETUP: + if (event.u.setup.bRequestType & USB_DIR_IN) + write(ep0, NULL, 0); + else + read(ep0, NULL, 0); + break; + + case FUNCTIONFS_ENABLE: + *ready = true; + break; + + case FUNCTIONFS_DISABLE: + *ready = false; + break; + + default: + break; + } +} + +void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len) +{ + unsigned i; + iobuf->buf = malloc(n*sizeof(*iobuf->buf)); + iobuf->iocb = malloc(n*sizeof(*iobuf->iocb)); + iobuf->cnt = n; + iobuf->len = len; + iobuf->requested = 0; + for (i = 0; i < n; ++i) { + iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf)); + iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb)); + } + iobuf->cnt = n; +} + +void delete_bufs(struct io_buffer *iobuf) +{ + unsigned i; + for (i = 0; i < iobuf->cnt; ++i) { + free(iobuf->buf[i]); + free(iobuf->iocb[i]); + } + free(iobuf->buf); + free(iobuf->iocb); +} + +int main(int argc, char *argv[]) +{ + int ret; + unsigned i, j; + char *ep_path; + + int ep0, ep1; + + io_context_t ctx; + + int evfd; + fd_set rfds; + + struct io_buffer iobuf[2]; + int actual = 0; + bool ready; + + if (argc != 2) { + printf("ffs directory not specified!\n"); + return 1; + } + + ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */); + if (!ep_path) { + perror("malloc"); + return 1; + } + + /* open endpoint files */ + sprintf(ep_path, "%s/ep0", argv[1]); + ep0 = open(ep_path, O_RDWR); + if (ep0 < 0) { + perror("unable to open ep0"); + return 1; + } + if (write(ep0, &descriptors, sizeof(descriptors)) < 0) { + perror("unable do write descriptors"); + return 1; + } + if (write(ep0, &strings, sizeof(strings)) < 0) { + perror("unable to write strings"); + return 1; + } + sprintf(ep_path, "%s/ep1", argv[1]); + ep1 = open(ep_path, O_RDWR); + if (ep1 < 0) { + perror("unable to open ep1"); + return 1; + } + + free(ep_path); + + memset(&ctx, 0, sizeof(ctx)); + /* setup aio context to handle up to AIO_MAX requests */ + if (io_setup(AIO_MAX, &ctx) < 0) { + perror("unable to setup aio"); + return 1; + } + + evfd = eventfd(0, 0); + if (evfd < 0) { + perror("unable to open eventfd"); + return 1; + } + + for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) + init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN); + + while (1) { + FD_ZERO(&rfds); + FD_SET(ep0, &rfds); + FD_SET(evfd, &rfds); + + ret = select(((ep0 > evfd) ? ep0 : evfd)+1, + &rfds, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EINTR) + continue; + perror("select"); + break; + } + + if (FD_ISSET(ep0, &rfds)) + handle_ep0(ep0, &ready); + + /* we are waiting for function ENABLE */ + if (!ready) + continue; + + /* + * when we're preparing new data to submit, + * second buffer being transmitted + */ + for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) { + if (iobuf[i].requested) + continue; + /* prepare requests */ + for (j = 0; j < iobuf[i].cnt; ++j) { + io_prep_pwrite(iobuf[i].iocb[j], ep1, + iobuf[i].buf[j], + iobuf[i].len, 0); + /* enable eventfd notification */ + iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD; + iobuf[i].iocb[j]->u.c.resfd = evfd; + } + /* submit table of requests */ + ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb); + if (ret >= 0) { + iobuf[i].requested = ret; + printf("submit: %d requests buf: %d\n", ret, i); + } else + perror("unable to submit requests"); + } + + /* if event is ready to read */ + if (!FD_ISSET(evfd, &rfds)) + continue; + + uint64_t ev_cnt; + ret = read(evfd, &ev_cnt, sizeof(ev_cnt)); + if (ret < 0) { + perror("unable to read eventfd"); + break; + } + + struct io_event e[BUFS_MAX]; + /* we read aio events */ + ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL); + if (ret > 0) /* if we got events */ + iobuf[actual].requested -= ret; + + /* if all req's from iocb completed */ + if (!iobuf[actual].requested) + actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf)); + } + + /* free resources */ + + for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) + delete_bufs(&iobuf[i]); + io_destroy(ctx); + + close(ep1); + close(ep0); + + return 0; +} 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 0000000000..df36e4c28f --- /dev/null +++ b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +CC = gcc +LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0) +LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0) +WARNINGS = -Wall -Wextra +CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS) +LDFLAGS = $(LIBUSB_LIBS) + +all: test +%: %.c + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +clean: + $(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 0000000000..2cbcce6e8d --- /dev/null +++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c @@ -0,0 +1,175 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + +#include <libusb.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define VENDOR 0x1d6b +#define PRODUCT 0x0105 + +#define BUF_LEN 8192 + +/* + * struct test_state - describes test program state + * @list: list of devices returned by libusb_get_device_list function + * @found: pointer to struct describing tested device + * @ctx: context, set to NULL + * @handle: handle of tested device + * @attached: indicates that device was attached to kernel, and has to be + * reattached at the end of test program + */ + +struct test_state { + libusb_device *found; + libusb_context *ctx; + libusb_device_handle *handle; + int attached; +}; + +/* + * test_init - initialize test program + */ + +int test_init(struct test_state *state) +{ + int i, ret; + ssize_t cnt; + libusb_device **list; + + state->found = NULL; + state->ctx = NULL; + state->handle = NULL; + state->attached = 0; + + ret = libusb_init(&state->ctx); + if (ret) { + printf("cannot init libusb: %s\n", libusb_error_name(ret)); + return 1; + } + + cnt = libusb_get_device_list(state->ctx, &list); + if (cnt <= 0) { + printf("no devices found\n"); + goto error1; + } + + for (i = 0; i < cnt; ++i) { + libusb_device *dev = list[i]; + struct libusb_device_descriptor desc; + ret = libusb_get_device_descriptor(dev, &desc); + if (ret) { + printf("unable to get device descriptor: %s\n", + libusb_error_name(ret)); + goto error2; + } + if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) { + state->found = dev; + break; + } + } + + if (!state->found) { + printf("no devices found\n"); + goto error2; + } + + ret = libusb_open(state->found, &state->handle); + if (ret) { + printf("cannot open device: %s\n", libusb_error_name(ret)); + goto error2; + } + + if (libusb_claim_interface(state->handle, 0)) { + ret = libusb_detach_kernel_driver(state->handle, 0); + if (ret) { + printf("unable to detach kernel driver: %s\n", + libusb_error_name(ret)); + goto error3; + } + state->attached = 1; + ret = libusb_claim_interface(state->handle, 0); + if (ret) { + printf("cannot claim interface: %s\n", + libusb_error_name(ret)); + goto error4; + } + } + + return 0; + +error4: + if (state->attached == 1) + libusb_attach_kernel_driver(state->handle, 0); + +error3: + libusb_close(state->handle); + +error2: + libusb_free_device_list(list, 1); + +error1: + libusb_exit(state->ctx); + return 1; +} + +/* + * test_exit - cleanup test program + */ + +void test_exit(struct test_state *state) +{ + libusb_release_interface(state->handle, 0); + if (state->attached == 1) + libusb_attach_kernel_driver(state->handle, 0); + libusb_close(state->handle); + libusb_exit(state->ctx); +} + +int main(void) +{ + struct test_state state; + struct libusb_config_descriptor *conf; + struct libusb_interface_descriptor const *iface; + unsigned char addr; + + if (test_init(&state)) + return 1; + + libusb_get_config_descriptor(state.found, 0, &conf); + iface = &conf->interface[0].altsetting[0]; + addr = iface->endpoint[0].bEndpointAddress; + + while (1) { + static unsigned char buffer[BUF_LEN]; + int bytes; + libusb_bulk_transfer(state.handle, addr, buffer, BUF_LEN, + &bytes, 500); + } + test_exit(&state); +} 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 0000000000..96616eb460 --- /dev/null +++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c @@ -0,0 +1,386 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + +/* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */ + +#define _DEFAULT_SOURCE /* for endian.h */ + +#include <endian.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/poll.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/eventfd.h> + +#include "libaio.h" +#define IOCB_FLAG_RESFD (1 << 0) + +#include <linux/usb/functionfs.h> + +#define BUF_LEN 8192 + +/* + * cpu_to_le16/32 are used when initializing structures, a context where a + * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way + * that allows them to be used when initializing structures. + */ + +#if BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#else +#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8)) +#define cpu_to_le32(x) \ + ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \ + (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24)) +#endif + +/******************** Descriptors and Strings *******************************/ + +static const struct { + struct usb_functionfs_descs_head_v2 header; + __le32 fs_count; + __le32 hs_count; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio bulk_sink; + struct usb_endpoint_descriptor_no_audio bulk_source; + } __attribute__ ((__packed__)) fs_descs, hs_descs; +} __attribute__ ((__packed__)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), + .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC), + .length = cpu_to_le32(sizeof(descriptors)), + }, + .fs_count = cpu_to_le32(3), + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = 1, + }, + .bulk_sink = { + .bLength = sizeof(descriptors.fs_descs.bulk_sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .bulk_source = { + .bLength = sizeof(descriptors.fs_descs.bulk_source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + }, + .hs_count = cpu_to_le32(3), + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = 1, + }, + .bulk_sink = { + .bLength = sizeof(descriptors.hs_descs.bulk_sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + }, + .bulk_source = { + .bLength = sizeof(descriptors.hs_descs.bulk_source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + }, + }, +}; + +#define STR_INTERFACE "AIO Test" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE)]; + } __attribute__ ((__packed__)) lang0; +} __attribute__ ((__packed__)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE, + }, +}; + +/******************** Endpoints handling *******************************/ + +static void display_event(struct usb_functionfs_event *event) +{ + static const char *const names[] = { + [FUNCTIONFS_BIND] = "BIND", + [FUNCTIONFS_UNBIND] = "UNBIND", + [FUNCTIONFS_ENABLE] = "ENABLE", + [FUNCTIONFS_DISABLE] = "DISABLE", + [FUNCTIONFS_SETUP] = "SETUP", + [FUNCTIONFS_SUSPEND] = "SUSPEND", + [FUNCTIONFS_RESUME] = "RESUME", + }; + switch (event->type) { + case FUNCTIONFS_BIND: + case FUNCTIONFS_UNBIND: + case FUNCTIONFS_ENABLE: + case FUNCTIONFS_DISABLE: + case FUNCTIONFS_SETUP: + case FUNCTIONFS_SUSPEND: + case FUNCTIONFS_RESUME: + printf("Event %s\n", names[event->type]); + } +} + +static void handle_ep0(int ep0, bool *ready) +{ + struct usb_functionfs_event event; + int ret; + + struct pollfd pfds[1]; + pfds[0].fd = ep0; + pfds[0].events = POLLIN; + + ret = poll(pfds, 1, 0); + + if (ret && (pfds[0].revents & POLLIN)) { + ret = read(ep0, &event, sizeof(event)); + if (!ret) { + perror("unable to read event from ep0"); + return; + } + display_event(&event); + switch (event.type) { + case FUNCTIONFS_SETUP: + if (event.u.setup.bRequestType & USB_DIR_IN) + write(ep0, NULL, 0); + else + read(ep0, NULL, 0); + break; + + case FUNCTIONFS_ENABLE: + *ready = true; + break; + + case FUNCTIONFS_DISABLE: + *ready = false; + break; + + default: + break; + } + } +} + +int main(int argc, char *argv[]) +{ + int i, ret; + char *ep_path; + + int ep0; + int ep[2]; + + io_context_t ctx; + + int evfd; + fd_set rfds; + + char *buf_in, *buf_out; + struct iocb *iocb_in, *iocb_out; + int req_in = 0, req_out = 0; + bool ready; + + if (argc != 2) { + printf("ffs directory not specified!\n"); + return 1; + } + + ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */); + if (!ep_path) { + perror("malloc"); + return 1; + } + + /* open endpoint files */ + sprintf(ep_path, "%s/ep0", argv[1]); + ep0 = open(ep_path, O_RDWR); + if (ep0 < 0) { + perror("unable to open ep0"); + return 1; + } + if (write(ep0, &descriptors, sizeof(descriptors)) < 0) { + perror("unable do write descriptors"); + return 1; + } + if (write(ep0, &strings, sizeof(strings)) < 0) { + perror("unable to write strings"); + return 1; + } + for (i = 0; i < 2; ++i) { + sprintf(ep_path, "%s/ep%d", argv[1], i+1); + ep[i] = open(ep_path, O_RDWR); + if (ep[i] < 0) { + printf("unable to open ep%d: %s\n", i+1, + strerror(errno)); + return 1; + } + } + + free(ep_path); + + memset(&ctx, 0, sizeof(ctx)); + /* setup aio context to handle up to 2 requests */ + if (io_setup(2, &ctx) < 0) { + perror("unable to setup aio"); + return 1; + } + + evfd = eventfd(0, 0); + if (evfd < 0) { + perror("unable to open eventfd"); + return 1; + } + + /* alloc buffers and requests */ + buf_in = malloc(BUF_LEN); + buf_out = malloc(BUF_LEN); + iocb_in = malloc(sizeof(*iocb_in)); + iocb_out = malloc(sizeof(*iocb_out)); + + while (1) { + FD_ZERO(&rfds); + FD_SET(ep0, &rfds); + FD_SET(evfd, &rfds); + + ret = select(((ep0 > evfd) ? ep0 : evfd)+1, + &rfds, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EINTR) + continue; + perror("select"); + break; + } + + if (FD_ISSET(ep0, &rfds)) + handle_ep0(ep0, &ready); + + /* we are waiting for function ENABLE */ + if (!ready) + continue; + + /* if something was submitted we wait for event */ + if (FD_ISSET(evfd, &rfds)) { + uint64_t ev_cnt; + ret = read(evfd, &ev_cnt, sizeof(ev_cnt)); + if (ret < 0) { + perror("unable to read eventfd"); + break; + } + + struct io_event e[2]; + /* we wait for one event */ + ret = io_getevents(ctx, 1, 2, e, NULL); + /* if we got event */ + for (i = 0; i < ret; ++i) { + if (e[i].obj->aio_fildes == ep[0]) { + printf("ev=in; ret=%lu\n", e[i].res); + req_in = 0; + } else if (e[i].obj->aio_fildes == ep[1]) { + printf("ev=out; ret=%lu\n", e[i].res); + req_out = 0; + } + } + } + + if (!req_in) { /* if IN transfer not requested*/ + /* prepare write request */ + io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0); + /* enable eventfd notification */ + iocb_in->u.c.flags |= IOCB_FLAG_RESFD; + iocb_in->u.c.resfd = evfd; + /* submit table of requests */ + ret = io_submit(ctx, 1, &iocb_in); + if (ret >= 0) { /* if ret > 0 request is queued */ + req_in = 1; + printf("submit: in\n"); + } else + perror("unable to submit request"); + } + if (!req_out) { /* if OUT transfer not requested */ + /* prepare read request */ + io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0); + /* enable eventfs notification */ + iocb_out->u.c.flags |= IOCB_FLAG_RESFD; + iocb_out->u.c.resfd = evfd; + /* submit table of requests */ + ret = io_submit(ctx, 1, &iocb_out); + if (ret >= 0) { /* if ret > 0 request is queued */ + req_out = 1; + printf("submit: out\n"); + } else + perror("unable to submit request"); + } + } + + /* free resources */ + + io_destroy(ctx); + + free(buf_in); + free(buf_out); + free(iocb_in); + free(iocb_out); + + for (i = 0; i < 2; ++i) + close(ep[i]); + close(ep0); + + return 0; +} 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 0000000000..c3523837c9 --- /dev/null +++ b/tools/usb/ffs-aio-example/simple/host_app/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +CC = gcc +LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0) +LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0) +WARNINGS = -Wall -Wextra +CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS) +LDFLAGS = $(LIBUSB_LIBS) + +all: test +%: %.c + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +clean: + $(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 0000000000..aed86ffff2 --- /dev/null +++ b/tools/usb/ffs-aio-example/simple/host_app/test.c @@ -0,0 +1,178 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + +#include <libusb.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define VENDOR 0x1d6b +#define PRODUCT 0x0105 + +#define BUF_LEN 8192 + +/* + * struct test_state - describes test program state + * @list: list of devices returned by libusb_get_device_list function + * @found: pointer to struct describing tested device + * @ctx: context, set to NULL + * @handle: handle of tested device + * @attached: indicates that device was attached to kernel, and has to be + * reattached at the end of test program + */ + +struct test_state { + libusb_device *found; + libusb_context *ctx; + libusb_device_handle *handle; + int attached; +}; + +/* + * test_init - initialize test program + */ + +int test_init(struct test_state *state) +{ + int i, ret; + ssize_t cnt; + libusb_device **list; + + state->found = NULL; + state->ctx = NULL; + state->handle = NULL; + state->attached = 0; + + ret = libusb_init(&state->ctx); + if (ret) { + printf("cannot init libusb: %s\n", libusb_error_name(ret)); + return 1; + } + + cnt = libusb_get_device_list(state->ctx, &list); + if (cnt <= 0) { + printf("no devices found\n"); + goto error1; + } + + for (i = 0; i < cnt; ++i) { + libusb_device *dev = list[i]; + struct libusb_device_descriptor desc; + ret = libusb_get_device_descriptor(dev, &desc); + if (ret) { + printf("unable to get device descriptor: %s\n", + libusb_error_name(ret)); + goto error2; + } + if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) { + state->found = dev; + break; + } + } + + if (!state->found) { + printf("no devices found\n"); + goto error2; + } + + ret = libusb_open(state->found, &state->handle); + if (ret) { + printf("cannot open device: %s\n", libusb_error_name(ret)); + goto error2; + } + + if (libusb_claim_interface(state->handle, 0)) { + ret = libusb_detach_kernel_driver(state->handle, 0); + if (ret) { + printf("unable to detach kernel driver: %s\n", + libusb_error_name(ret)); + goto error3; + } + state->attached = 1; + ret = libusb_claim_interface(state->handle, 0); + if (ret) { + printf("cannot claim interface: %s\n", + libusb_error_name(ret)); + goto error4; + } + } + + return 0; + +error4: + if (state->attached == 1) + libusb_attach_kernel_driver(state->handle, 0); + +error3: + libusb_close(state->handle); + +error2: + libusb_free_device_list(list, 1); + +error1: + libusb_exit(state->ctx); + return 1; +} + +/* + * test_exit - cleanup test program + */ + +void test_exit(struct test_state *state) +{ + libusb_release_interface(state->handle, 0); + if (state->attached == 1) + libusb_attach_kernel_driver(state->handle, 0); + libusb_close(state->handle); + libusb_exit(state->ctx); +} + +int main(void) +{ + struct test_state state; + struct libusb_config_descriptor *conf; + struct libusb_interface_descriptor const *iface; + unsigned char in_addr, out_addr; + + if (test_init(&state)) + return 1; + + libusb_get_config_descriptor(state.found, 0, &conf); + iface = &conf->interface[0].altsetting[0]; + in_addr = iface->endpoint[0].bEndpointAddress; + out_addr = iface->endpoint[1].bEndpointAddress; + + while (1) { + static unsigned char buffer[BUF_LEN]; + int bytes; + libusb_bulk_transfer(state.handle, in_addr, buffer, BUF_LEN, + &bytes, 500); + libusb_bulk_transfer(state.handle, out_addr, buffer, BUF_LEN, + &bytes, 500); + } + test_exit(&state); +} |