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 <[email protected]>
> ---
> 
> 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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to