diff options
Diffstat (limited to 'tools/usb/ffs-aio-example/multibuff')
3 files changed, 508 insertions, 0 deletions
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 000000000000..87216a0c4a8b --- /dev/null +++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c | |||
| @@ -0,0 +1,349 @@ | |||
| 1 | #define _BSD_SOURCE /* for endian.h */ | ||
| 2 | |||
| 3 | #include <endian.h> | ||
| 4 | #include <errno.h> | ||
| 5 | #include <fcntl.h> | ||
| 6 | #include <stdarg.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <sys/ioctl.h> | ||
| 11 | #include <sys/stat.h> | ||
| 12 | #include <sys/types.h> | ||
| 13 | #include <sys/poll.h> | ||
| 14 | #include <unistd.h> | ||
| 15 | #include <stdbool.h> | ||
| 16 | #include <sys/eventfd.h> | ||
| 17 | |||
| 18 | #include "libaio.h" | ||
| 19 | #define IOCB_FLAG_RESFD (1 << 0) | ||
| 20 | |||
| 21 | #include <linux/usb/functionfs.h> | ||
| 22 | |||
| 23 | #define BUF_LEN 8192 | ||
| 24 | #define BUFS_MAX 128 | ||
| 25 | #define AIO_MAX (BUFS_MAX*2) | ||
| 26 | |||
| 27 | /******************** Descriptors and Strings *******************************/ | ||
| 28 | |||
| 29 | static const struct { | ||
| 30 | struct usb_functionfs_descs_head header; | ||
| 31 | struct { | ||
| 32 | struct usb_interface_descriptor intf; | ||
| 33 | struct usb_endpoint_descriptor_no_audio bulk_sink; | ||
| 34 | struct usb_endpoint_descriptor_no_audio bulk_source; | ||
| 35 | } __attribute__ ((__packed__)) fs_descs, hs_descs; | ||
| 36 | } __attribute__ ((__packed__)) descriptors = { | ||
| 37 | .header = { | ||
| 38 | .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC), | ||
| 39 | .length = htole32(sizeof(descriptors)), | ||
| 40 | .fs_count = 3, | ||
| 41 | .hs_count = 3, | ||
| 42 | }, | ||
| 43 | .fs_descs = { | ||
| 44 | .intf = { | ||
| 45 | .bLength = sizeof(descriptors.fs_descs.intf), | ||
| 46 | .bDescriptorType = USB_DT_INTERFACE, | ||
| 47 | .bNumEndpoints = 2, | ||
| 48 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | ||
| 49 | .iInterface = 1, | ||
| 50 | }, | ||
| 51 | .bulk_sink = { | ||
| 52 | .bLength = sizeof(descriptors.fs_descs.bulk_sink), | ||
| 53 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 54 | .bEndpointAddress = 1 | USB_DIR_IN, | ||
| 55 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 56 | }, | ||
| 57 | .bulk_source = { | ||
| 58 | .bLength = sizeof(descriptors.fs_descs.bulk_source), | ||
| 59 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 60 | .bEndpointAddress = 2 | USB_DIR_OUT, | ||
| 61 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 62 | }, | ||
| 63 | }, | ||
| 64 | .hs_descs = { | ||
| 65 | .intf = { | ||
| 66 | .bLength = sizeof(descriptors.hs_descs.intf), | ||
| 67 | .bDescriptorType = USB_DT_INTERFACE, | ||
| 68 | .bNumEndpoints = 2, | ||
| 69 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | ||
| 70 | .iInterface = 1, | ||
| 71 | }, | ||
| 72 | .bulk_sink = { | ||
| 73 | .bLength = sizeof(descriptors.hs_descs.bulk_sink), | ||
| 74 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 75 | .bEndpointAddress = 1 | USB_DIR_IN, | ||
| 76 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 77 | .wMaxPacketSize = htole16(512), | ||
| 78 | }, | ||
| 79 | .bulk_source = { | ||
| 80 | .bLength = sizeof(descriptors.hs_descs.bulk_source), | ||
| 81 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 82 | .bEndpointAddress = 2 | USB_DIR_OUT, | ||
| 83 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 84 | .wMaxPacketSize = htole16(512), | ||
| 85 | }, | ||
| 86 | }, | ||
| 87 | }; | ||
| 88 | |||
| 89 | #define STR_INTERFACE "AIO Test" | ||
| 90 | |||
| 91 | static const struct { | ||
| 92 | struct usb_functionfs_strings_head header; | ||
| 93 | struct { | ||
| 94 | __le16 code; | ||
| 95 | const char str1[sizeof(STR_INTERFACE)]; | ||
| 96 | } __attribute__ ((__packed__)) lang0; | ||
| 97 | } __attribute__ ((__packed__)) strings = { | ||
| 98 | .header = { | ||
| 99 | .magic = htole32(FUNCTIONFS_STRINGS_MAGIC), | ||
| 100 | .length = htole32(sizeof(strings)), | ||
| 101 | .str_count = htole32(1), | ||
| 102 | .lang_count = htole32(1), | ||
| 103 | }, | ||
| 104 | .lang0 = { | ||
| 105 | htole16(0x0409), /* en-us */ | ||
| 106 | STR_INTERFACE, | ||
| 107 | }, | ||
| 108 | }; | ||
| 109 | |||
| 110 | /********************** Buffer structure *******************************/ | ||
| 111 | |||
| 112 | struct io_buffer { | ||
| 113 | struct iocb **iocb; | ||
| 114 | unsigned char **buf; | ||
| 115 | unsigned cnt; | ||
| 116 | unsigned len; | ||
| 117 | unsigned requested; | ||
| 118 | }; | ||
| 119 | |||
| 120 | /******************** Endpoints handling *******************************/ | ||
| 121 | |||
| 122 | static void display_event(struct usb_functionfs_event *event) | ||
| 123 | { | ||
| 124 | static const char *const names[] = { | ||
| 125 | [FUNCTIONFS_BIND] = "BIND", | ||
| 126 | [FUNCTIONFS_UNBIND] = "UNBIND", | ||
| 127 | [FUNCTIONFS_ENABLE] = "ENABLE", | ||
| 128 | [FUNCTIONFS_DISABLE] = "DISABLE", | ||
| 129 | [FUNCTIONFS_SETUP] = "SETUP", | ||
| 130 | [FUNCTIONFS_SUSPEND] = "SUSPEND", | ||
| 131 | [FUNCTIONFS_RESUME] = "RESUME", | ||
| 132 | }; | ||
| 133 | switch (event->type) { | ||
| 134 | case FUNCTIONFS_BIND: | ||
| 135 | case FUNCTIONFS_UNBIND: | ||
| 136 | case FUNCTIONFS_ENABLE: | ||
| 137 | case FUNCTIONFS_DISABLE: | ||
| 138 | case FUNCTIONFS_SETUP: | ||
| 139 | case FUNCTIONFS_SUSPEND: | ||
| 140 | case FUNCTIONFS_RESUME: | ||
| 141 | printf("Event %s\n", names[event->type]); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | static void handle_ep0(int ep0, bool *ready) | ||
| 146 | { | ||
| 147 | int ret; | ||
| 148 | struct usb_functionfs_event event; | ||
| 149 | |||
| 150 | ret = read(ep0, &event, sizeof(event)); | ||
| 151 | if (!ret) { | ||
| 152 | perror("unable to read event from ep0"); | ||
| 153 | return; | ||
| 154 | } | ||
| 155 | display_event(&event); | ||
| 156 | switch (event.type) { | ||
| 157 | case FUNCTIONFS_SETUP: | ||
| 158 | if (event.u.setup.bRequestType & USB_DIR_IN) | ||
| 159 | write(ep0, NULL, 0); | ||
| 160 | else | ||
| 161 | read(ep0, NULL, 0); | ||
| 162 | break; | ||
| 163 | |||
| 164 | case FUNCTIONFS_ENABLE: | ||
| 165 | *ready = true; | ||
| 166 | break; | ||
| 167 | |||
| 168 | case FUNCTIONFS_DISABLE: | ||
| 169 | *ready = false; | ||
| 170 | break; | ||
| 171 | |||
| 172 | default: | ||
| 173 | break; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len) | ||
| 178 | { | ||
| 179 | unsigned i; | ||
| 180 | iobuf->buf = malloc(n*sizeof(*iobuf->buf)); | ||
| 181 | iobuf->iocb = malloc(n*sizeof(*iobuf->iocb)); | ||
| 182 | iobuf->cnt = n; | ||
| 183 | iobuf->len = len; | ||
| 184 | iobuf->requested = 0; | ||
| 185 | for (i = 0; i < n; ++i) { | ||
| 186 | iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf)); | ||
| 187 | iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb)); | ||
| 188 | } | ||
| 189 | iobuf->cnt = n; | ||
| 190 | } | ||
| 191 | |||
| 192 | void delete_bufs(struct io_buffer *iobuf) | ||
| 193 | { | ||
| 194 | unsigned i; | ||
| 195 | for (i = 0; i < iobuf->cnt; ++i) { | ||
| 196 | free(iobuf->buf[i]); | ||
| 197 | free(iobuf->iocb[i]); | ||
| 198 | } | ||
| 199 | free(iobuf->buf); | ||
| 200 | free(iobuf->iocb); | ||
| 201 | } | ||
| 202 | |||
| 203 | int main(int argc, char *argv[]) | ||
| 204 | { | ||
| 205 | int ret; | ||
| 206 | unsigned i, j; | ||
| 207 | char *ep_path; | ||
| 208 | |||
| 209 | int ep0, ep1; | ||
| 210 | |||
| 211 | io_context_t ctx; | ||
| 212 | |||
| 213 | int evfd; | ||
| 214 | fd_set rfds; | ||
| 215 | |||
| 216 | struct io_buffer iobuf[2]; | ||
| 217 | int actual = 0; | ||
| 218 | bool ready; | ||
| 219 | |||
| 220 | if (argc != 2) { | ||
| 221 | printf("ffs directory not specified!\n"); | ||
| 222 | return 1; | ||
| 223 | } | ||
| 224 | |||
| 225 | ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */); | ||
| 226 | if (!ep_path) { | ||
| 227 | perror("malloc"); | ||
| 228 | return 1; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* open endpoint files */ | ||
| 232 | sprintf(ep_path, "%s/ep0", argv[1]); | ||
| 233 | ep0 = open(ep_path, O_RDWR); | ||
| 234 | if (ep0 < 0) { | ||
| 235 | perror("unable to open ep0"); | ||
| 236 | return 1; | ||
| 237 | } | ||
| 238 | if (write(ep0, &descriptors, sizeof(descriptors)) < 0) { | ||
| 239 | perror("unable do write descriptors"); | ||
| 240 | return 1; | ||
| 241 | } | ||
| 242 | if (write(ep0, &strings, sizeof(strings)) < 0) { | ||
| 243 | perror("unable to write strings"); | ||
| 244 | return 1; | ||
| 245 | } | ||
| 246 | sprintf(ep_path, "%s/ep1", argv[1]); | ||
| 247 | ep1 = open(ep_path, O_RDWR); | ||
| 248 | if (ep1 < 0) { | ||
| 249 | perror("unable to open ep1"); | ||
| 250 | return 1; | ||
| 251 | } | ||
| 252 | |||
| 253 | free(ep_path); | ||
| 254 | |||
| 255 | memset(&ctx, 0, sizeof(ctx)); | ||
| 256 | /* setup aio context to handle up to AIO_MAX requests */ | ||
| 257 | if (io_setup(AIO_MAX, &ctx) < 0) { | ||
| 258 | perror("unable to setup aio"); | ||
| 259 | return 1; | ||
| 260 | } | ||
| 261 | |||
| 262 | evfd = eventfd(0, 0); | ||
| 263 | if (evfd < 0) { | ||
| 264 | perror("unable to open eventfd"); | ||
| 265 | return 1; | ||
| 266 | } | ||
| 267 | |||
| 268 | for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) | ||
| 269 | init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN); | ||
| 270 | |||
| 271 | while (1) { | ||
| 272 | FD_ZERO(&rfds); | ||
| 273 | FD_SET(ep0, &rfds); | ||
| 274 | FD_SET(evfd, &rfds); | ||
| 275 | |||
| 276 | ret = select(((ep0 > evfd) ? ep0 : evfd)+1, | ||
| 277 | &rfds, NULL, NULL, NULL); | ||
| 278 | if (ret < 0) { | ||
| 279 | if (errno == EINTR) | ||
| 280 | continue; | ||
| 281 | perror("select"); | ||
| 282 | break; | ||
| 283 | } | ||
| 284 | |||
| 285 | if (FD_ISSET(ep0, &rfds)) | ||
| 286 | handle_ep0(ep0, &ready); | ||
| 287 | |||
| 288 | /* we are waiting for function ENABLE */ | ||
| 289 | if (!ready) | ||
| 290 | continue; | ||
| 291 | |||
| 292 | /* | ||
| 293 | * when we're preparing new data to submit, | ||
| 294 | * second buffer being transmitted | ||
| 295 | */ | ||
| 296 | for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) { | ||
| 297 | if (iobuf[i].requested) | ||
| 298 | continue; | ||
| 299 | /* prepare requests */ | ||
| 300 | for (j = 0; j < iobuf[i].cnt; ++j) { | ||
| 301 | io_prep_pwrite(iobuf[i].iocb[j], ep1, | ||
| 302 | iobuf[i].buf[j], | ||
| 303 | iobuf[i].len, 0); | ||
| 304 | /* enable eventfd notification */ | ||
| 305 | iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD; | ||
| 306 | iobuf[i].iocb[j]->u.c.resfd = evfd; | ||
| 307 | } | ||
| 308 | /* submit table of requests */ | ||
| 309 | ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb); | ||
| 310 | if (ret >= 0) { | ||
| 311 | iobuf[i].requested = ret; | ||
| 312 | printf("submit: %d requests buf: %d\n", ret, i); | ||
| 313 | } else | ||
| 314 | perror("unable to submit reqests"); | ||
| 315 | } | ||
| 316 | |||
| 317 | /* if event is ready to read */ | ||
| 318 | if (!FD_ISSET(evfd, &rfds)) | ||
| 319 | continue; | ||
| 320 | |||
| 321 | uint64_t ev_cnt; | ||
| 322 | ret = read(evfd, &ev_cnt, sizeof(ev_cnt)); | ||
| 323 | if (ret < 0) { | ||
| 324 | perror("unable to read eventfd"); | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | |||
| 328 | struct io_event e[BUFS_MAX]; | ||
| 329 | /* we read aio events */ | ||
| 330 | ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL); | ||
| 331 | if (ret > 0) /* if we got events */ | ||
| 332 | iobuf[actual].requested -= ret; | ||
| 333 | |||
| 334 | /* if all req's from iocb completed */ | ||
| 335 | if (!iobuf[actual].requested) | ||
| 336 | actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf)); | ||
| 337 | } | ||
| 338 | |||
| 339 | /* free resources */ | ||
| 340 | |||
| 341 | for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) | ||
| 342 | delete_bufs(&iobuf[i]); | ||
| 343 | io_destroy(ctx); | ||
| 344 | |||
| 345 | close(ep1); | ||
| 346 | close(ep0); | ||
| 347 | |||
| 348 | return 0; | ||
| 349 | } | ||
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 000000000000..8c4a6f0aa82d --- /dev/null +++ b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | CC = gcc | ||
| 2 | LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0) | ||
| 3 | LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0) | ||
| 4 | WARNINGS = -Wall -Wextra | ||
| 5 | CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS) | ||
| 6 | LDFLAGS = $(LIBUSB_LIBS) | ||
| 7 | |||
| 8 | all: test | ||
| 9 | %: %.c | ||
| 10 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) | ||
| 11 | |||
| 12 | clean: | ||
| 13 | $(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 000000000000..b0ad8747d03f --- /dev/null +++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #include <libusb.h> | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <unistd.h> | ||
| 5 | |||
| 6 | #define VENDOR 0x1d6b | ||
| 7 | #define PRODUCT 0x0105 | ||
| 8 | |||
| 9 | /* endpoints indexes */ | ||
| 10 | |||
| 11 | #define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN) | ||
| 12 | #define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT) | ||
| 13 | |||
| 14 | #define BUF_LEN 8192 | ||
| 15 | |||
| 16 | /* | ||
| 17 | * struct test_state - describes test program state | ||
| 18 | * @list: list of devices returned by libusb_get_device_list function | ||
| 19 | * @found: pointer to struct describing tested device | ||
| 20 | * @ctx: context, set to NULL | ||
| 21 | * @handle: handle of tested device | ||
| 22 | * @attached: indicates that device was attached to kernel, and has to be | ||
| 23 | * reattached at the end of test program | ||
| 24 | */ | ||
| 25 | |||
| 26 | struct test_state { | ||
| 27 | libusb_device *found; | ||
| 28 | libusb_context *ctx; | ||
| 29 | libusb_device_handle *handle; | ||
| 30 | int attached; | ||
| 31 | }; | ||
| 32 | |||
| 33 | /* | ||
| 34 | * test_init - initialize test program | ||
| 35 | */ | ||
| 36 | |||
| 37 | int test_init(struct test_state *state) | ||
| 38 | { | ||
| 39 | int i, ret; | ||
| 40 | ssize_t cnt; | ||
| 41 | libusb_device **list; | ||
| 42 | |||
| 43 | state->found = NULL; | ||
| 44 | state->ctx = NULL; | ||
| 45 | state->handle = NULL; | ||
| 46 | state->attached = 0; | ||
| 47 | |||
| 48 | ret = libusb_init(&state->ctx); | ||
| 49 | if (ret) { | ||
| 50 | printf("cannot init libusb: %s\n", libusb_error_name(ret)); | ||
| 51 | return 1; | ||
| 52 | } | ||
| 53 | |||
| 54 | cnt = libusb_get_device_list(state->ctx, &list); | ||
| 55 | if (cnt <= 0) { | ||
| 56 | printf("no devices found\n"); | ||
| 57 | goto error1; | ||
| 58 | } | ||
| 59 | |||
| 60 | for (i = 0; i < cnt; ++i) { | ||
| 61 | libusb_device *dev = list[i]; | ||
| 62 | struct libusb_device_descriptor desc; | ||
| 63 | ret = libusb_get_device_descriptor(dev, &desc); | ||
| 64 | if (ret) { | ||
| 65 | printf("unable to get device descriptor: %s\n", | ||
| 66 | libusb_error_name(ret)); | ||
| 67 | goto error2; | ||
| 68 | } | ||
| 69 | if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) { | ||
| 70 | state->found = dev; | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | if (!state->found) { | ||
| 76 | printf("no devices found\n"); | ||
| 77 | goto error2; | ||
| 78 | } | ||
| 79 | |||
| 80 | ret = libusb_open(state->found, &state->handle); | ||
| 81 | if (ret) { | ||
| 82 | printf("cannot open device: %s\n", libusb_error_name(ret)); | ||
| 83 | goto error2; | ||
| 84 | } | ||
| 85 | |||
| 86 | if (libusb_claim_interface(state->handle, 0)) { | ||
| 87 | ret = libusb_detach_kernel_driver(state->handle, 0); | ||
| 88 | if (ret) { | ||
| 89 | printf("unable to detach kernel driver: %s\n", | ||
| 90 | libusb_error_name(ret)); | ||
| 91 | goto error3; | ||
| 92 | } | ||
| 93 | state->attached = 1; | ||
| 94 | ret = libusb_claim_interface(state->handle, 0); | ||
| 95 | if (ret) { | ||
| 96 | printf("cannot claim interface: %s\n", | ||
| 97 | libusb_error_name(ret)); | ||
| 98 | goto error4; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | return 0; | ||
| 103 | |||
| 104 | error4: | ||
| 105 | if (state->attached == 1) | ||
| 106 | libusb_attach_kernel_driver(state->handle, 0); | ||
| 107 | |||
| 108 | error3: | ||
| 109 | libusb_close(state->handle); | ||
| 110 | |||
| 111 | error2: | ||
| 112 | libusb_free_device_list(list, 1); | ||
| 113 | |||
| 114 | error1: | ||
| 115 | libusb_exit(state->ctx); | ||
| 116 | return 1; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* | ||
| 120 | * test_exit - cleanup test program | ||
| 121 | */ | ||
| 122 | |||
| 123 | void test_exit(struct test_state *state) | ||
| 124 | { | ||
| 125 | libusb_release_interface(state->handle, 0); | ||
| 126 | if (state->attached == 1) | ||
| 127 | libusb_attach_kernel_driver(state->handle, 0); | ||
| 128 | libusb_close(state->handle); | ||
| 129 | libusb_exit(state->ctx); | ||
| 130 | } | ||
| 131 | |||
| 132 | int main(void) | ||
| 133 | { | ||
| 134 | struct test_state state; | ||
| 135 | |||
| 136 | if (test_init(&state)) | ||
| 137 | return 1; | ||
| 138 | |||
| 139 | while (1) { | ||
| 140 | static unsigned char buffer[BUF_LEN]; | ||
| 141 | int bytes; | ||
| 142 | libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN, | ||
| 143 | &bytes, 500); | ||
| 144 | } | ||
| 145 | test_exit(&state); | ||
| 146 | } | ||
