[PATCH v3] 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 third version of patch adding examples of use AIO API of FunctionFS.
>From last version of this patch, I have fixed few bugs and style problems.

Changelog:

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

 tools/usb/aio_multibuff/device_app/aio_multibuff.c |  322 ++++++++++++++++++++
 tools/usb/aio_multibuff/host_app/Makefile          |   13 +
 tools/usb/aio_multibuff/host_app/test.c            |  142 +++++++++
 tools/usb/aio_simple/device_app/aio_simple.c       |  296 ++++++++++++++++++
 tools/usb/aio_simple/host_app/Makefile             |   13 +
 tools/usb/aio_simple/host_app/test.c               |  145 +++++++++
 6 files changed, 931 insertions(+)
 create mode 100644 tools/usb/aio_multibuff/device_app/aio_multibuff.c
 create mode 100644 tools/usb/aio_multibuff/host_app/Makefile
 create mode 100644 tools/usb/aio_multibuff/host_app/test.c
 create mode 100644 tools/usb/aio_simple/device_app/aio_simple.c
 create mode 100644 tools/usb/aio_simple/host_app/Makefile
 create mode 100644 tools/usb/aio_simple/host_app/test.c

diff --git a/tools/usb/aio_multibuff/device_app/aio_multibuff.c b/tools/usb/aio_multibuff/device_app/aio_multibuff.c
new file mode 100644
index 0000000..0c5bf82
--- /dev/null
+++ b/tools/usb/aio_multibuff/device_app/aio_multibuff.c
@@ -0,0 +1,322 @@
+#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 "libaio.h"
+
+#include <linux/usb/functionfs.h>
+
+/******************** 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]);
+	default:
+		break;
+	}
+}
+
+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;
+		}
+	}
+}
+
+struct io_buffer {
+	struct iocb **iocb;
+	unsigned char **buf;
+	int cnt;
+	int len;
+};
+
+void init_bufs(struct io_buffer *iobuf, int n, int len)
+{
+	int i;
+	iobuf->buf = malloc(n*sizeof(*iobuf->buf));
+	iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
+	iobuf->cnt = n;
+	iobuf->len = len;
+	for (i = 0; i < n; ++i) {
+		iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
+		iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
+	}
+}
+
+void delete_bufs(struct io_buffer *iobuf)
+{
+	int i;
+	for (i = 0; i < iobuf->cnt; ++i) {
+		free(iobuf->buf[i]);
+		free(iobuf->iocb[i]);
+	}
+	free(iobuf->buf);
+	free(iobuf->iocb);
+}
+
+#define BUF_LEN		8192
+#define BUFS_MAX	128
+#define AIO_MAX		(BUFS_MAX*2)
+
+int main(int argc, char *argv[])
+{
+	int i, ret;
+	char *ep_path;
+
+	int ep0, ep1;
+
+	io_context_t ctx;
+
+	struct io_buffer iobuf1, iobuf2;
+	int requested1 = 0, requested2 = 0;
+	int actual;
+	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;
+	}
+
+	init_bufs(&iobuf1, BUFS_MAX, BUF_LEN);
+	init_bufs(&iobuf2, BUFS_MAX, BUF_LEN);
+
+	while (1) {
+		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
+		 */
+		if (!requested1) { /* if all req's from iocb1 completed */
+			actual = 2;
+			for (i = 0; i < iobuf1.cnt; ++i) /* prepare requests */
+				io_prep_pwrite(iobuf1.iocb[i], ep1,
+					       iobuf1.buf[i], iobuf1.len, 0);
+			/* submit table of requests */
+			ret = io_submit(ctx, iobuf1.cnt, iobuf1.iocb);
+			if (ret >= 0) {
+				requested1 = ret;
+				printf("submit: %d requests from buf 1\n", ret);
+			} else
+				perror("unable to submit reqests for buf2");
+		}
+		if (!requested2) { /* if all req's from iocb2 completed */
+			actual = 1;
+			for (i = 0; i < iobuf2.cnt; ++i) /* prepare requests */
+				io_prep_pwrite(iobuf2.iocb[i], ep1,
+					       iobuf2.buf[i], iobuf2.len, 0);
+			/* submit table of requests */
+			ret = io_submit(ctx, iobuf2.cnt, iobuf2.iocb);
+			if (ret >= 0) {
+				requested2 = ret;
+				printf("submit: %d requests from buf 2\n", ret);
+			} else
+				perror("unable to submit reqests for buf2");
+		}
+		/* if something was submitted we wait for event */
+		if (requested1 || requested2) {
+			struct io_event e;
+			struct timespec timeout = {0, 1000};
+			/* we wait for one event */
+			ret = io_getevents(ctx, 1, 1, &e, &timeout);
+			if (ret > 0) { /* if we got event */
+				if (actual == 1)
+					requested1--;
+				else
+					requested2--;
+			}
+		}
+	}
+
+	/* free resources */
+
+	delete_bufs(&iobuf1);
+	delete_bufs(&iobuf2);
+	io_destroy(ctx);
+
+	close(ep1);
+	close(ep0);
+
+	return 0;
+}
diff --git a/tools/usb/aio_multibuff/host_app/Makefile b/tools/usb/aio_multibuff/host_app/Makefile
new file mode 100644
index 0000000..8c4a6f0
--- /dev/null
+++ b/tools/usb/aio_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/aio_multibuff/host_app/test.c b/tools/usb/aio_multibuff/host_app/test.c
new file mode 100644
index 0000000..5d4f4e5
--- /dev/null
+++ b/tools/usb/aio_multibuff/host_app/test.c
@@ -0,0 +1,142 @@
+#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)
+
+/*
+ * 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;
+	ssize_t cnt;
+	libusb_device **list;
+
+	state->found = NULL;
+	state->ctx = NULL;
+	state->handle = NULL;
+	state->attached = 0;
+
+	if (libusb_init(&state->ctx)) {
+		printf("libusb init failed\n");
+		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;
+		if (libusb_get_device_descriptor(dev, &desc)) {
+			printf("unable to get device descriptor\n");
+			goto error1;
+		}
+		if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) {
+			state->found = dev;
+			break;
+		}
+	}
+
+	if (state->found) {
+		printf("found device\n");
+
+		printf("open device: ");
+		if (libusb_open(state->found, &state->handle)) {
+			printf("ERROR\n");
+			goto error1;
+		}
+		printf("DONE\n");
+
+		if (libusb_kernel_driver_active(state->handle, 0)) {
+			printf("device busy.. detaching\n");
+			if (libusb_detach_kernel_driver(state->handle, 0)) {
+				printf("unable do deatch device\n");
+				goto error2;
+			}
+			state->attached = 1;
+		} else
+			printf("device free from kernel\n");
+
+		if (libusb_claim_interface(state->handle, 0)) {
+			printf("failed to claim interface\n");
+			goto error3;
+		}
+	} else {
+		printf("no devices found\n");
+		goto error1;
+	}
+	return 0;
+
+error3:
+	if (state->attached == 1)
+		libusb_attach_kernel_driver(state->handle, 0);
+
+error2:
+	libusb_close(state->handle);
+
+error1:
+	libusb_free_device_list(list, 1);
+	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);
+}
+
+#define BUF_LEN	8192
+
+int main()
+{
+	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/aio_simple/device_app/aio_simple.c b/tools/usb/aio_simple/device_app/aio_simple.c
new file mode 100644
index 0000000..aa9a38c
--- /dev/null
+++ b/tools/usb/aio_simple/device_app/aio_simple.c
@@ -0,0 +1,296 @@
+#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 "libaio.h"
+
+#include <linux/usb/functionfs.h>
+
+/******************** 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]);
+	default:
+		break;
+	}
+}
+
+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;
+		}
+	}
+}
+
+#define BUF_LEN		8192
+
+int main(int argc, char *argv[])
+{
+	int i, ret;
+	char *ep_path;
+
+	int ep0;
+	int ep[2];
+
+	io_context_t ctx;
+
+	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;
+	}
+
+	/* 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) {
+		handle_ep0(ep0, &ready);
+		/* we are waiting for function ENABLE */
+		if (!ready)
+			continue;
+		if (!req_in) { /* if IN transfer not requested*/
+			/* prepare write request */
+			io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
+			/* 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);
+			/* 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");
+		}
+		/* if something was submitted we wait for event */
+		if (req_in || req_out) {
+			struct io_event e;
+			struct timespec timeout = {0, 1000};
+			/* we wait for one event */
+			ret = io_getevents(ctx, 1, 1, &e, &timeout);
+			if (ret > 0) { /* if we got event */
+				if (e.obj->aio_fildes == ep[0]) {
+					printf("event=in; ret=%lu\n", e.res);
+					req_in = 0;
+				}
+				if (e.obj->aio_fildes == ep[1]) {
+					printf("event=out; ret=%lu\n", e.res);
+					req_out = 0;
+				}
+			}
+		}
+	}
+
+	/* 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/aio_simple/host_app/Makefile b/tools/usb/aio_simple/host_app/Makefile
new file mode 100644
index 0000000..8c4a6f0
--- /dev/null
+++ b/tools/usb/aio_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/aio_simple/host_app/test.c b/tools/usb/aio_simple/host_app/test.c
new file mode 100644
index 0000000..964c1db
--- /dev/null
+++ b/tools/usb/aio_simple/host_app/test.c
@@ -0,0 +1,145 @@
+#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)
+
+/*
+ * 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;
+	ssize_t cnt;
+	libusb_device **list;
+
+	state->found = NULL;
+	state->ctx = NULL;
+	state->handle = NULL;
+	state->attached = 0;
+
+	if (libusb_init(&state->ctx)) {
+		printf("libusb init failed\n");
+		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;
+		if (libusb_get_device_descriptor(dev, &desc)) {
+			printf("unable to get device descriptor\n");
+			goto error1;
+		}
+		if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) {
+			state->found = dev;
+			break;
+		}
+	}
+
+	if (state->found) {
+		printf("found device\n");
+
+		printf("open device: ");
+		if (libusb_open(state->found, &state->handle)) {
+			printf("ERROR\n");
+			goto error1;
+		}
+		printf("DONE\n");
+
+		if (libusb_kernel_driver_active(state->handle, 0)) {
+			printf("device busy.. detaching\n");
+			if (libusb_detach_kernel_driver(state->handle, 0)) {
+				printf("unable do deatch device\n");
+				goto error2;
+			}
+			state->attached = 1;
+		} else
+			printf("device free from kernel\n");
+
+		if (libusb_claim_interface(state->handle, 0)) {
+			printf("failed to claim interface\n");
+			goto error3;
+		}
+	} else {
+		printf("no devices found\n");
+		goto error1;
+	}
+
+	return 0;
+
+error3:
+	if (state->attached == 1)
+		libusb_attach_kernel_driver(state->handle, 0);
+
+error2:
+	libusb_close(state->handle);
+
+error1:
+	libusb_free_device_list(list, 1);
+	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);
+}
+
+#define BUF_LEN	8192
+
+int main()
+{
+	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