diff options
Diffstat (limited to 'tools/usb/ffs-aio-example/simple')
-rw-r--r-- | tools/usb/ffs-aio-example/simple/device_app/aio_simple.c | 368 | ||||
-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 |
3 files changed, 560 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 000000000..1f44a2981 --- /dev/null +++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c @@ -0,0 +1,368 @@ +/* + * 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 + +/******************** 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, + }, +}; + +/******************** 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 000000000..c3523837c --- /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 000000000..aed86ffff --- /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); +} |