[PATCH v5] tools: usb: aio example applications

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

 



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);
+}
-- 
1.7.9.5

--
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