Re: [PATCH v5] tools: usb: aio example applications

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux