Hi Felipe, Are you going to merge this examples, or should they be merged through another tree? Best regards Robert Baldyga Samsung R&D Institute Poland On 02/11/2014 11:43 AM, Robert Baldyga wrote: > 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@xxxxxxxxxxx> > --- > > Hello, > > This is fifth version of patch adding examples of use AIO API of FunctionFS. > > In this version I have fixed eventfd handling adding read() when any events > available. I also added reading many events at once, and made some changes > increasing readability of code. > > Best regards > Robert Baldyga > Samsung R&D Institute Poland > > Changelog: > > v5: > - move examples to diretory tools/usb/ffs-aio-example > - fix eventfd handling > - add receiveing many events and once > - improve error handling > > v4: http://www.spinics.net/lists/linux-usb/msg102195.html > - fix error handling in host applications > - replace busy waiting with select function > - cleanup code, remove duplicated code, move structure definitions > and #defines to top on file > > v3: http://www.spinics.net/lists/linux-usb/msg101723.html > - get rid of global variables > - add missing error handling > - fix some style problems > > v2: http://www.spinics.net/lists/linux-usb/msg101650.html > - cleanup code > - a lot of small fixes > > v1: http://www.spinics.net/lists/linux-usb/msg101614.html > > .../multibuff/device_app/aio_multibuff.c | 349 ++++++++++++++++++++ > .../ffs-aio-example/multibuff/host_app/Makefile | 13 + > .../usb/ffs-aio-example/multibuff/host_app/test.c | 146 ++++++++ > .../ffs-aio-example/simple/device_app/aio_simple.c | 335 +++++++++++++++++++ > tools/usb/ffs-aio-example/simple/host_app/Makefile | 13 + > tools/usb/ffs-aio-example/simple/host_app/test.c | 148 +++++++++ > 6 files changed, 1004 insertions(+) > create mode 100644 tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c > create mode 100644 tools/usb/ffs-aio-example/multibuff/host_app/Makefile > create mode 100644 tools/usb/ffs-aio-example/multibuff/host_app/test.c > create mode 100644 tools/usb/ffs-aio-example/simple/device_app/aio_simple.c > create mode 100644 tools/usb/ffs-aio-example/simple/host_app/Makefile > create mode 100644 tools/usb/ffs-aio-example/simple/host_app/test.c > > 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 0000000..87216a0 > --- /dev/null > +++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c > @@ -0,0 +1,349 @@ > +#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 header; > + 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), > + .length = htole32(sizeof(descriptors)), > + .fs_count = 3, > + .hs_count = 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_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 reqests"); > + } > + > + /* 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 0000000..8c4a6f0 > --- /dev/null > +++ b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile > @@ -0,0 +1,13 @@ > +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 0000000..b0ad874 > --- /dev/null > +++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c > @@ -0,0 +1,146 @@ > +#include <libusb.h> > +#include <stdio.h> > +#include <string.h> > +#include <unistd.h> > + > +#define VENDOR 0x1d6b > +#define PRODUCT 0x0105 > + > +/* endpoints indexes */ > + > +#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN) > +#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT) > + > +#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; > + > + if (test_init(&state)) > + return 1; > + > + while (1) { > + static unsigned char buffer[BUF_LEN]; > + int bytes; > + libusb_bulk_transfer(state.handle, EP_BULK_IN, 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 0000000..f558664 > --- /dev/null > +++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c > @@ -0,0 +1,335 @@ > +#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 header; > + 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), > + .length = htole32(sizeof(descriptors)), > + .fs_count = 3, > + .hs_count = 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_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, > + }, > + .bulk_source = { > + .bLength = sizeof(descriptors.hs_descs.bulk_source), > + .bDescriptorType = USB_DT_ENDPOINT, > + .bEndpointAddress = 2 | USB_DIR_OUT, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + }, > + }, > +}; > + > +#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 0000000..8c4a6f0 > --- /dev/null > +++ b/tools/usb/ffs-aio-example/simple/host_app/Makefile > @@ -0,0 +1,13 @@ > +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 0000000..64b6a57 > --- /dev/null > +++ b/tools/usb/ffs-aio-example/simple/host_app/test.c > @@ -0,0 +1,148 @@ > +#include <libusb.h> > +#include <stdio.h> > +#include <string.h> > +#include <unistd.h> > + > +#define VENDOR 0x1d6b > +#define PRODUCT 0x0105 > + > +/* endpoints indexes */ > + > +#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN) > +#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT) > + > +#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; > + > + if (test_init(&state)) > + return 1; > + > + while (1) { > + static unsigned char buffer[BUF_LEN]; > + int bytes; > + libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN, > + &bytes, 500); > + libusb_bulk_transfer(state.handle, EP_BULK_OUT, buffer, BUF_LEN, > + &bytes, 500); > + } > + test_exit(&state); > +} > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html