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 second version of patch adding examples of use AIO API of FunctionFS. >From last version of this patch, I have cleaned up code and fixed bugs and style problems pointed by Michal Nazarewicz and Peter Stuge. Changelog: v2: - 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 | 301 ++++++++++++++++++++ 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 | 279 ++++++++++++++++++ tools/usb/aio_simple/host_app/Makefile | 13 + tools/usb/aio_simple/host_app/test.c | 145 ++++++++++ 6 files changed, 893 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..f4ae701 --- /dev/null +++ b/tools/usb/aio_multibuff/device_app/aio_multibuff.c @@ -0,0 +1,301 @@ +#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(struct usb_functionfs_event)); + if (!ret) + 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 +#define BUFS_MAX 128 +#define AIO_MAX (BUFS_MAX*2) + +struct iocb *iocb1[AIO_MAX]; +struct iocb *iocb2[AIO_MAX]; + +unsigned char *buf1[BUFS_MAX]; +unsigned char *buf2[BUFS_MAX]; + +void init_bufs() +{ + int i; + for (i = 0; i < BUFS_MAX; ++i) { + buf1[i] = malloc(BUF_LEN); + buf2[i] = malloc(BUF_LEN); + } + for (i = 0; i < AIO_MAX; ++i) { + iocb1[i] = malloc(sizeof(**iocb1)); + iocb2[i] = malloc(sizeof(**iocb2)); + } +} + +void delete_bufs() +{ + int i; + for (i = 0; i < BUFS_MAX; ++i) { + free(buf1[i]); + free(buf2[i]); + } + for (i = 0; i < AIO_MAX; ++i) { + free(iocb1[i]); + free(iocb2[i]); + } +} + +int main(int argc, char *argv[]) +{ + int i, ret; + char ep_path[64]; + + int ep0, ep1; + + io_context_t ctx; + + int requested1 = 0, requested2 = 0; + int actual; + bool ready; + + if (argc != 2) { + printf("ffs directory not specified!\n"); + return 1; + } + + /* open endpoint files */ + snprintf(ep_path, sizeof(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; + } + snprintf(ep_path, sizeof(ep_path), "%s/ep1", argv[1]); + ep1 = open(ep_path, O_RDWR); + if (ep1 < 0) { + perror("unable to open ep1"); + return 1; + } + + memset(&ctx, 0, sizeof(ctx)); + /* setup aio context to handle up to AIO_MAX requests */ + io_setup(AIO_MAX, &ctx); + + init_bufs(); + + 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 < BUFS_MAX; ++i) /* prepare requests */ + io_prep_pwrite(iocb1[i], ep1, buf1[i], + BUF_LEN, 0); + /* submit table of requests */ + ret = io_submit(ctx, BUFS_MAX, iocb1); + requested1 = ret; + printf("submit: %d requests from buf 1\n", ret); + } + if (!requested2) { /* if all req's from iocb2 completed */ + actual = 1; + for (i = 0; i < BUFS_MAX; ++i) /* prepare requests */ + io_prep_pwrite(iocb2[i], ep1, buf2[i], + BUF_LEN, 0); + /* submit table of requests */ + ret = io_submit(ctx, BUFS_MAX, iocb2); + requested2 = ret; + printf("submit: %d requests from buf 2\n", ret); + } + /* 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(); + 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..5ab404f --- /dev/null +++ b/tools/usb/aio_simple/device_app/aio_simple.c @@ -0,0 +1,279 @@ +#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(struct usb_functionfs_event)); + if (!ret) + 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[64]; + + 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; + } + + /* open endpoint files */ + snprintf(ep_path, sizeof(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) { + snprintf(ep_path, sizeof(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; + } + } + + /* 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)); + + memset(&ctx, 0, sizeof(ctx)); + io_setup(2, &ctx); /* setup aio context to handle up to 2 requests */ + + 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"); + } + } + 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"); + } + } + /* 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