summaryrefslogtreecommitdiffstats
path: root/tools/usb
diff options
context:
space:
mode:
authorNiklas Cassel <cassel@kernel.org>2026-09-16 15:00:32 +0200
committerNiklas Cassel <cassel@kernel.org>2026-09-16 16:18:00 +0200
commit8d836581f9b1f57ceaa2b47654754ef1260b410b (patch)
tree1dbd4d0dfb99c2e238356cbe947e5b0be3e3a571 /tools/usb
downloadlinux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.tar.gz
linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.zip
ata: libata-scsi: fix ata_dsm_trim_pages() kernel-docgrafted
make htmldocs fails with: Documentation/driver-api/libata:604: ./drivers/ata/libata-scsi.c:2301: ERROR: Unexpected indentation. [docutils] The Return: section of ata_dsm_trim_pages() ends its first sentence with a colon and continues with an indented bullet list. reStructuredText requires a blank line before an indented block, so docutils chokes on the list. Simply adding the missing blank line does not work either: Return: is a kernel-doc "special section", which is terminated by the first blank line, so the bullet list would end up in the Description section, detached from the sentence introducing it. Spell the two bounds out as prose instead, so that the Return: section stays self-contained. Documentation-only change. Fixes: e64e6b5dc867 ("ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND") Reported-by: Thomas Huth <thuth@redhat.com> Closes: https://lore.kernel.org/linux-ide/b0a0b8e8-cc4f-4c7b-8bc5-fee0712d405a@redhat.com/ Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Link: https://lore.kernel.org/r/20260916130031.29990-2-cassel@kernel.org Signed-off-by: Niklas Cassel <cassel@kernel.org>
Diffstat (limited to 'tools/usb')
-rw-r--r--tools/usb/.gitignore3
-rw-r--r--tools/usb/Build2
-rw-r--r--tools/usb/Makefile51
-rw-r--r--tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c380
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/Makefile14
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/test.c175
-rw-r--r--tools/usb/ffs-aio-example/simple/device_app/aio_simple.c386
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/Makefile14
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/test.c178
-rw-r--r--tools/usb/ffs-test.c681
-rw-r--r--tools/usb/hcd-tests.sh274
-rwxr-xr-xtools/usb/p9_fwd.py243
-rw-r--r--tools/usb/testusb.c534
-rw-r--r--tools/usb/usbip/.gitignore33
-rw-r--r--tools/usb/usbip/AUTHORS3
-rw-r--r--tools/usb/usbip/COPYING340
-rw-r--r--tools/usb/usbip/INSTALL237
-rw-r--r--tools/usb/usbip/Makefile.am8
-rw-r--r--tools/usb/usbip/README255
-rwxr-xr-xtools/usb/usbip/autogen.sh9
-rwxr-xr-xtools/usb/usbip/cleanup.sh13
-rw-r--r--tools/usb/usbip/configure.ac111
-rw-r--r--tools/usb/usbip/doc/usbip.8137
-rw-r--r--tools/usb/usbip/doc/usbipd.8117
-rw-r--r--tools/usb/usbip/libsrc/Makefile.am11
-rw-r--r--tools/usb/usbip/libsrc/list.h137
-rw-r--r--tools/usb/usbip/libsrc/names.c486
-rw-r--r--tools/usb/usbip/libsrc/names.h26
-rw-r--r--tools/usb/usbip/libsrc/sysfs_utils.c32
-rw-r--r--tools/usb/usbip/libsrc/sysfs_utils.h9
-rw-r--r--tools/usb/usbip/libsrc/usbip_common.c320
-rw-r--r--tools/usb/usbip/libsrc/usbip_common.h152
-rw-r--r--tools/usb/usbip/libsrc/usbip_device_driver.c162
-rw-r--r--tools/usb/usbip/libsrc/usbip_device_driver.h22
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_common.c285
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_common.h92
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_driver.c54
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_driver.h20
-rw-r--r--tools/usb/usbip/libsrc/vhci_driver.c468
-rw-r--r--tools/usb/usbip/libsrc/vhci_driver.h67
-rw-r--r--tools/usb/usbip/src/Makefile.am12
-rw-r--r--tools/usb/usbip/src/usbip.c191
-rw-r--r--tools/usb/usbip/src/usbip.h28
-rw-r--r--tools/usb/usbip/src/usbip_attach.c243
-rw-r--r--tools/usb/usbip/src/usbip_bind.c211
-rw-r--r--tools/usb/usbip/src/usbip_detach.c125
-rw-r--r--tools/usb/usbip/src/usbip_list.c373
-rw-r--r--tools/usb/usbip/src/usbip_network.c303
-rw-r--r--tools/usb/usbip/src/usbip_network.h178
-rw-r--r--tools/usb/usbip/src/usbip_port.c57
-rw-r--r--tools/usb/usbip/src/usbip_unbind.c129
-rw-r--r--tools/usb/usbip/src/usbipd.c686
-rw-r--r--tools/usb/usbip/src/utils.c43
-rw-r--r--tools/usb/usbip/src/utils.h13
-rwxr-xr-xtools/usb/usbip/vudc/vudc_server_example.sh107
55 files changed, 9240 insertions, 0 deletions
diff --git a/tools/usb/.gitignore b/tools/usb/.gitignore
new file mode 100644
index 000000000..fce1ef5a9
--- /dev/null
+++ b/tools/usb/.gitignore
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+ffs-test
+testusb
diff --git a/tools/usb/Build b/tools/usb/Build
new file mode 100644
index 000000000..2ad6f9745
--- /dev/null
+++ b/tools/usb/Build
@@ -0,0 +1,2 @@
+testusb-y += testusb.o
+ffs-test-y += ffs-test.o
diff --git a/tools/usb/Makefile b/tools/usb/Makefile
new file mode 100644
index 000000000..c6235667d
--- /dev/null
+++ b/tools/usb/Makefile
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for USB tools
+include ../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -Wextra -g -D_GNU_SOURCE -I$(OUTPUT)include -I$(srctree)/tools/include
+override LDFLAGS += -lpthread
+
+ALL_TARGETS := testusb ffs-test
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+TESTUSB_IN := $(OUTPUT)testusb-in.o
+$(TESTUSB_IN): FORCE
+ $(Q)$(MAKE) $(build)=testusb
+$(OUTPUT)testusb: $(TESTUSB_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
+
+FFS_TEST_IN := $(OUTPUT)ffs-test-in.o
+$(FFS_TEST_IN): FORCE
+ $(Q)$(MAKE) $(build)=ffs-test
+$(OUTPUT)ffs-test: $(FFS_TEST_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
+
+clean:
+ rm -f $(ALL_PROGRAMS)
+ find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.o.cmd' -delete
+
+install: $(ALL_PROGRAMS)
+ install -d -m 755 $(DESTDIR)$(bindir); \
+ for program in $(ALL_PROGRAMS); do \
+ install $$program $(DESTDIR)$(bindir); \
+ done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
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 000000000..aaca1f44e
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
@@ -0,0 +1,380 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
+#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_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
+ 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_V2),
+ .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
+ .length = htole32(sizeof(descriptors)),
+ },
+ .fs_count = htole32(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_count = htole32(3),
+ .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 requests");
+ }
+
+ /* 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 000000000..df36e4c28
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+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 000000000..2cbcce6e8
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
@@ -0,0 +1,175 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
+#include <libusb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define VENDOR 0x1d6b
+#define PRODUCT 0x0105
+
+#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;
+ struct libusb_config_descriptor *conf;
+ struct libusb_interface_descriptor const *iface;
+ unsigned char addr;
+
+ if (test_init(&state))
+ return 1;
+
+ libusb_get_config_descriptor(state.found, 0, &conf);
+ iface = &conf->interface[0].altsetting[0];
+ addr = iface->endpoint[0].bEndpointAddress;
+
+ while (1) {
+ static unsigned char buffer[BUF_LEN];
+ int bytes;
+ libusb_bulk_transfer(state.handle, addr, 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 000000000..96616eb46
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
@@ -0,0 +1,386 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
+/* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */
+
+#define _DEFAULT_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
+
+/*
+ * cpu_to_le16/32 are used when initializing structures, a context where a
+ * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way
+ * that allows them to be used when initializing structures.
+ */
+
+#if BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#else
+#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
+#define cpu_to_le32(x) \
+ ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
+ (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
+#endif
+
+/******************** Descriptors and Strings *******************************/
+
+static const struct {
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
+ 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 = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
+ .length = cpu_to_le32(sizeof(descriptors)),
+ },
+ .fs_count = cpu_to_le32(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_count = cpu_to_le32(3),
+ .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 = cpu_to_le16(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 = cpu_to_le16(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 = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof(strings)),
+ .str_count = cpu_to_le32(1),
+ .lang_count = cpu_to_le32(1),
+ },
+ .lang0 = {
+ cpu_to_le16(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 000000000..c3523837c
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/host_app/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+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 000000000..aed86ffff
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/host_app/test.c
@@ -0,0 +1,178 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
+#include <libusb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define VENDOR 0x1d6b
+#define PRODUCT 0x0105
+
+#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;
+ struct libusb_config_descriptor *conf;
+ struct libusb_interface_descriptor const *iface;
+ unsigned char in_addr, out_addr;
+
+ if (test_init(&state))
+ return 1;
+
+ libusb_get_config_descriptor(state.found, 0, &conf);
+ iface = &conf->interface[0].altsetting[0];
+ in_addr = iface->endpoint[0].bEndpointAddress;
+ out_addr = iface->endpoint[1].bEndpointAddress;
+
+ while (1) {
+ static unsigned char buffer[BUF_LEN];
+ int bytes;
+ libusb_bulk_transfer(state.handle, in_addr, buffer, BUF_LEN,
+ &bytes, 500);
+ libusb_bulk_transfer(state.handle, out_addr, buffer, BUF_LEN,
+ &bytes, 500);
+ }
+ test_exit(&state);
+}
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
new file mode 100644
index 000000000..22b938fbd
--- /dev/null
+++ b/tools/usb/ffs-test.c
@@ -0,0 +1,681 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ffs-test.c -- user mode filesystem api for usb composite function
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ */
+
+/* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
+
+
+#define _DEFAULT_SOURCE /* for endian.h */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <tools/le_byteshift.h>
+
+#include "../../include/uapi/linux/usb/functionfs.h"
+
+
+/******************** Little Endian Handling ********************************/
+
+/*
+ * cpu_to_le16/32 are used when initializing structures, a context where a
+ * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way
+ * that allows them to be used when initializing structures.
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#else
+#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
+#define cpu_to_le32(x) \
+ ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
+ (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
+#endif
+
+#define le32_to_cpu(x) le32toh(x)
+#define le16_to_cpu(x) le16toh(x)
+
+/******************** Messages and Errors ***********************************/
+
+static const char argv0[] = "ffs-test";
+
+static unsigned verbosity = 7;
+
+static void _msg(unsigned level, const char *fmt, ...)
+{
+ if (level < 2)
+ level = 2;
+ else if (level > 7)
+ level = 7;
+
+ if (level <= verbosity) {
+ static const char levels[8][6] = {
+ [2] = "crit:",
+ [3] = "err: ",
+ [4] = "warn:",
+ [5] = "note:",
+ [6] = "info:",
+ [7] = "dbg: "
+ };
+
+ int _errno = errno;
+ va_list ap;
+
+ fprintf(stderr, "%s: %s ", argv0, levels[level]);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[strlen(fmt) - 1] != '\n') {
+ char buffer[128];
+ strerror_r(_errno, buffer, sizeof buffer);
+ fprintf(stderr, ": (-%d) %s\n", _errno, buffer);
+ }
+
+ fflush(stderr);
+ }
+}
+
+#define die(...) (_msg(2, __VA_ARGS__), exit(1))
+#define err(...) _msg(3, __VA_ARGS__)
+#define warn(...) _msg(4, __VA_ARGS__)
+#define note(...) _msg(5, __VA_ARGS__)
+#define info(...) _msg(6, __VA_ARGS__)
+#define debug(...) _msg(7, __VA_ARGS__)
+
+#define die_on(cond, ...) do { \
+ if (cond) \
+ die(__VA_ARGS__); \
+ } while (0)
+
+
+/******************** Descriptors and Strings *******************************/
+
+static const struct {
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
+ __le32 ss_count;
+ struct {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio sink;
+ struct usb_endpoint_descriptor_no_audio source;
+ } __attribute__((packed)) fs_descs, hs_descs;
+ struct {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio sink;
+ struct usb_ss_ep_comp_descriptor sink_comp;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_ss_ep_comp_descriptor source_comp;
+ } ss_descs;
+} __attribute__((packed)) descriptors = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC),
+ .length = cpu_to_le32(sizeof descriptors),
+ },
+ .fs_count = cpu_to_le32(3),
+ .fs_descs = {
+ .intf = {
+ .bLength = sizeof descriptors.fs_descs.intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .sink = {
+ .bLength = sizeof descriptors.fs_descs.sink,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* .wMaxPacketSize = autoconfiguration (kernel) */
+ },
+ .source = {
+ .bLength = sizeof descriptors.fs_descs.source,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* .wMaxPacketSize = autoconfiguration (kernel) */
+ },
+ },
+ .hs_count = cpu_to_le32(3),
+ .hs_descs = {
+ .intf = {
+ .bLength = sizeof descriptors.fs_descs.intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .sink = {
+ .bLength = sizeof descriptors.hs_descs.sink,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+ },
+ .source = {
+ .bLength = sizeof descriptors.hs_descs.source,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+ .bInterval = 1, /* NAK every 1 uframe */
+ },
+ },
+ .ss_count = cpu_to_le32(5),
+ .ss_descs = {
+ .intf = {
+ .bLength = sizeof descriptors.fs_descs.intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .sink = {
+ .bLength = sizeof descriptors.hs_descs.sink,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+ },
+ .sink_comp = {
+ .bLength = USB_DT_SS_EP_COMP_SIZE,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = 0,
+ },
+ .source = {
+ .bLength = sizeof descriptors.hs_descs.source,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+ .bInterval = 1, /* NAK every 1 uframe */
+ },
+ .source_comp = {
+ .bLength = USB_DT_SS_EP_COMP_SIZE,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = 0,
+ },
+ },
+};
+
+static size_t descs_to_legacy(void **legacy, const void *descriptors_v2)
+{
+ const unsigned char *descs_end, *descs_start;
+ __u32 length, fs_count = 0, hs_count = 0, count;
+
+ /* Read v2 header */
+ {
+ const struct {
+ const struct usb_functionfs_descs_head_v2 header;
+ const __le32 counts[];
+ } __attribute__((packed)) *const in = descriptors_v2;
+ const __le32 *counts = in->counts;
+ __u32 flags;
+
+ if (le32_to_cpu(in->header.magic) !=
+ FUNCTIONFS_DESCRIPTORS_MAGIC_V2)
+ return 0;
+ length = le32_to_cpu(in->header.length);
+ if (length <= sizeof in->header)
+ return 0;
+ length -= sizeof in->header;
+ flags = le32_to_cpu(in->header.flags);
+ if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC))
+ return 0;
+
+#define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \
+ if (!(flags & (flg))) \
+ break; \
+ if (length < 4) \
+ return 0; \
+ ret = le32_to_cpu(*counts); \
+ length -= 4; \
+ ++counts; \
+ } while (0)
+
+ GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC);
+ GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC);
+ GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC);
+
+ count = fs_count + hs_count;
+ if (!count)
+ return 0;
+ descs_start = (const void *)counts;
+
+#undef GET_NEXT_COUNT_IF_FLAG
+ }
+
+ /*
+ * Find the end of FS and HS USB descriptors. SS descriptors
+ * are ignored since legacy format does not support them.
+ */
+ descs_end = descs_start;
+ do {
+ if (length < *descs_end)
+ return 0;
+ length -= *descs_end;
+ descs_end += *descs_end;
+ } while (--count);
+
+ /* Allocate legacy descriptors and copy the data. */
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ struct {
+ struct usb_functionfs_descs_head header;
+ __u8 descriptors[];
+ } __attribute__((packed)) *out;
+#pragma GCC diagnostic pop
+
+ length = sizeof out->header + (descs_end - descs_start);
+ out = malloc(length);
+ out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+ out->header.length = cpu_to_le32(length);
+ out->header.fs_count = cpu_to_le32(fs_count);
+ out->header.hs_count = cpu_to_le32(hs_count);
+ memcpy(out->descriptors, descs_start, descs_end - descs_start);
+ *legacy = out;
+ }
+
+ return length;
+}
+
+
+#define STR_INTERFACE_ "Source/Sink"
+
+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 = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof strings),
+ .str_count = cpu_to_le32(1),
+ .lang_count = cpu_to_le32(1),
+ },
+ .lang0 = {
+ cpu_to_le16(0x0409), /* en-us */
+ STR_INTERFACE_,
+ },
+};
+
+#define STR_INTERFACE strings.lang0.str1
+
+
+/******************** Files and Threads Handling ****************************/
+
+struct thread;
+
+static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes);
+static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes);
+static ssize_t ep0_consume(struct thread *t, const void *buf, size_t nbytes);
+static ssize_t fill_in_buf(struct thread *t, void *buf, size_t nbytes);
+static ssize_t empty_out_buf(struct thread *t, const void *buf, size_t nbytes);
+
+
+static struct thread {
+ const char *const filename;
+ size_t buf_size;
+
+ ssize_t (*in)(struct thread *, void *, size_t);
+ const char *const in_name;
+
+ ssize_t (*out)(struct thread *, const void *, size_t);
+ const char *const out_name;
+
+ int fd;
+ pthread_t id;
+ void *buf;
+ ssize_t status;
+} threads[] = {
+ {
+ "ep0", 4 * sizeof(struct usb_functionfs_event),
+ read_wrap, NULL,
+ ep0_consume, "<consume>",
+ 0, 0, NULL, 0
+ },
+ {
+ "ep1", 8 * 1024,
+ fill_in_buf, "<in>",
+ write_wrap, NULL,
+ 0, 0, NULL, 0
+ },
+ {
+ "ep2", 8 * 1024,
+ read_wrap, NULL,
+ empty_out_buf, "<out>",
+ 0, 0, NULL, 0
+ },
+};
+
+
+static void init_thread(struct thread *t)
+{
+ t->buf = malloc(t->buf_size);
+ die_on(!t->buf, "malloc");
+
+ t->fd = open(t->filename, O_RDWR);
+ die_on(t->fd < 0, "%s", t->filename);
+}
+
+static void cleanup_thread(void *arg)
+{
+ struct thread *t = arg;
+ int ret, fd;
+
+ fd = t->fd;
+ if (t->fd < 0)
+ return;
+ t->fd = -1;
+
+ /* test the FIFO ioctls (non-ep0 code paths) */
+ if (t != threads) {
+ ret = ioctl(fd, FUNCTIONFS_FIFO_STATUS);
+ if (ret < 0) {
+ /* ENODEV reported after disconnect */
+ if (errno != ENODEV)
+ err("%s: get fifo status", t->filename);
+ } else if (ret) {
+ warn("%s: unclaimed = %d\n", t->filename, ret);
+ if (ioctl(fd, FUNCTIONFS_FIFO_FLUSH) < 0)
+ err("%s: fifo flush", t->filename);
+ }
+ }
+
+ if (close(fd) < 0)
+ err("%s: close", t->filename);
+
+ free(t->buf);
+ t->buf = NULL;
+}
+
+static void *start_thread_helper(void *arg)
+{
+ const char *name, *op, *in_name, *out_name;
+ struct thread *t = arg;
+ ssize_t ret;
+
+ info("%s: starts\n", t->filename);
+ in_name = t->in_name ? t->in_name : t->filename;
+ out_name = t->out_name ? t->out_name : t->filename;
+
+ pthread_cleanup_push(cleanup_thread, arg);
+
+ for (;;) {
+ pthread_testcancel();
+
+ ret = t->in(t, t->buf, t->buf_size);
+ if (ret > 0) {
+ ret = t->out(t, t->buf, ret);
+ name = out_name;
+ op = "write";
+ } else {
+ name = in_name;
+ op = "read";
+ }
+
+ if (ret > 0) {
+ /* nop */
+ } else if (!ret) {
+ debug("%s: %s: EOF", name, op);
+ break;
+ } else if (errno == EINTR || errno == EAGAIN) {
+ debug("%s: %s", name, op);
+ } else {
+ warn("%s: %s", name, op);
+ break;
+ }
+ }
+
+ pthread_cleanup_pop(1);
+
+ t->status = ret;
+ info("%s: ends\n", t->filename);
+ return NULL;
+}
+
+static void start_thread(struct thread *t)
+{
+ debug("%s: starting\n", t->filename);
+
+ die_on(pthread_create(&t->id, NULL, start_thread_helper, t) < 0,
+ "pthread_create(%s)", t->filename);
+}
+
+static void join_thread(struct thread *t)
+{
+ int ret = pthread_join(t->id, NULL);
+
+ if (ret < 0)
+ err("%s: joining thread", t->filename);
+ else
+ debug("%s: joined\n", t->filename);
+}
+
+
+static ssize_t read_wrap(struct thread *t, void *buf, size_t nbytes)
+{
+ return read(t->fd, buf, nbytes);
+}
+
+static ssize_t write_wrap(struct thread *t, const void *buf, size_t nbytes)
+{
+ return write(t->fd, buf, nbytes);
+}
+
+
+/******************** Empty/Fill buffer routines ****************************/
+
+/* 0 -- stream of zeros, 1 -- i % 63, 2 -- pipe */
+enum pattern { PAT_ZERO, PAT_SEQ, PAT_PIPE };
+static enum pattern pattern;
+
+static ssize_t
+fill_in_buf(struct thread *ignore, void *buf, size_t nbytes)
+{
+ size_t i;
+ __u8 *p;
+
+ (void)ignore;
+
+ switch (pattern) {
+ case PAT_ZERO:
+ memset(buf, 0, nbytes);
+ break;
+
+ case PAT_SEQ:
+ for (p = buf, i = 0; i < nbytes; ++i, ++p)
+ *p = i % 63;
+ break;
+
+ case PAT_PIPE:
+ return fread(buf, 1, nbytes, stdin);
+ }
+
+ return nbytes;
+}
+
+static ssize_t
+empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
+{
+ const __u8 *p;
+ __u8 expected;
+ ssize_t ret;
+ size_t len;
+
+ (void)ignore;
+
+ switch (pattern) {
+ case PAT_ZERO:
+ expected = 0;
+ for (p = buf, len = 0; len < nbytes; ++p, ++len)
+ if (*p)
+ goto invalid;
+ break;
+
+ case PAT_SEQ:
+ for (p = buf, len = 0; len < nbytes; ++p, ++len)
+ if (*p != len % 63) {
+ expected = len % 63;
+ goto invalid;
+ }
+ break;
+
+ case PAT_PIPE:
+ ret = fwrite(buf, nbytes, 1, stdout);
+ if (ret > 0)
+ fflush(stdout);
+ break;
+
+invalid:
+ err("bad OUT byte %zd, expected %02x got %02x\n",
+ len, expected, *p);
+ for (p = buf, len = 0; len < nbytes; ++p, ++len) {
+ if (0 == (len % 32))
+ fprintf(stderr, "%4zd:", len);
+ fprintf(stderr, " %02x", *p);
+ if (31 == (len % 32))
+ fprintf(stderr, "\n");
+ }
+ fflush(stderr);
+ errno = EILSEQ;
+ return -1;
+ }
+
+ return len;
+}
+
+
+/******************** Endpoints routines ************************************/
+
+static void handle_setup(const struct usb_ctrlrequest *setup)
+{
+ printf("bRequestType = %d\n", setup->bRequestType);
+ printf("bRequest = %d\n", setup->bRequest);
+ printf("wValue = %d\n", le16_to_cpu(setup->wValue));
+ printf("wIndex = %d\n", le16_to_cpu(setup->wIndex));
+ printf("wLength = %d\n", le16_to_cpu(setup->wLength));
+}
+
+static ssize_t
+ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
+{
+ 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",
+ };
+
+ const struct usb_functionfs_event *event = buf;
+ size_t n;
+
+ (void)ignore;
+
+ for (n = nbytes / sizeof *event; n; --n, ++event)
+ 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]);
+ if (event->type == FUNCTIONFS_SETUP)
+ handle_setup(&event->u.setup);
+ break;
+
+ default:
+ printf("Event %03u (unknown)\n", event->type);
+ }
+
+ return nbytes;
+}
+
+static void ep0_init(struct thread *t, bool legacy_descriptors)
+{
+ void *legacy;
+ ssize_t ret;
+ size_t len;
+
+ if (legacy_descriptors) {
+ info("%s: writing descriptors\n", t->filename);
+ goto legacy;
+ }
+
+ info("%s: writing descriptors (in v2 format)\n", t->filename);
+ ret = write(t->fd, &descriptors, sizeof descriptors);
+
+ if (ret < 0 && errno == EINVAL) {
+ warn("%s: new format rejected, trying legacy\n", t->filename);
+legacy:
+ len = descs_to_legacy(&legacy, &descriptors);
+ if (len) {
+ ret = write(t->fd, legacy, len);
+ free(legacy);
+ }
+ }
+ die_on(ret < 0, "%s: write: descriptors", t->filename);
+
+ info("%s: writing strings\n", t->filename);
+ ret = write(t->fd, &strings, sizeof strings);
+ die_on(ret < 0, "%s: write: strings", t->filename);
+}
+
+
+/******************** Main **************************************************/
+
+int main(int argc, char **argv)
+{
+ bool legacy_descriptors;
+ unsigned i;
+
+ legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l");
+
+ init_thread(threads);
+ ep0_init(threads, legacy_descriptors);
+
+ for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+ init_thread(threads + i);
+
+ for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+ start_thread(threads + i);
+
+ start_thread_helper(threads);
+
+ for (i = 1; i < sizeof threads / sizeof *threads; ++i)
+ join_thread(threads + i);
+
+ return 0;
+}
diff --git a/tools/usb/hcd-tests.sh b/tools/usb/hcd-tests.sh
new file mode 100644
index 000000000..73f914d13
--- /dev/null
+++ b/tools/usb/hcd-tests.sh
@@ -0,0 +1,274 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# test types can be passed on the command line:
+#
+# - control: any device can do this
+# - out, in: out needs 'bulk sink' firmware, in needs 'bulk src'
+# - iso-out, iso-in: out needs 'iso sink' firmware, in needs 'iso src'
+# - halt: needs bulk sink+src, tests halt set/clear from host
+# - unlink: needs bulk sink and/or src, test HCD unlink processing
+# - loop: needs firmware that will buffer N transfers
+#
+# run it for hours, days, weeks.
+#
+
+#
+# this default provides a steady test load for a bulk device
+#
+TYPES='control out in'
+#TYPES='control out in halt'
+
+#
+# to test HCD code
+#
+# - include unlink tests
+# - add some ${RANDOM}ness
+# - connect several devices concurrently (same HC)
+# - keep HC's IRQ lines busy with unrelated traffic (IDE, net, ...)
+# - add other concurrent system loads
+#
+
+declare -i COUNT BUFLEN
+
+COUNT=50000
+BUFLEN=2048
+
+# NOTE: the 'in' and 'out' cases are usually bulk, but can be
+# set up to use interrupt transfers by 'usbtest' module options
+
+
+if [ "$DEVICE" = "" ]; then
+ echo "testing ALL recognized usbtest devices"
+ echo ""
+ TEST_ARGS="-a"
+else
+ TEST_ARGS=""
+fi
+
+do_test ()
+{
+ if ! ./testusb $TEST_ARGS -s $BUFLEN -c $COUNT $* 2>/dev/null
+ then
+ echo "FAIL"
+ exit 1
+ fi
+}
+
+ARGS="$*"
+
+if [ "$ARGS" = "" ];
+then
+ ARGS="$TYPES"
+fi
+
+# FIXME use /sys/bus/usb/device/$THIS/bConfigurationValue to
+# check and change configs
+
+CONFIG=''
+
+check_config ()
+{
+ if [ "$CONFIG" = "" ]; then
+ CONFIG=$1
+ echo "assuming $CONFIG configuration"
+ return
+ fi
+ if [ "$CONFIG" = $1 ]; then
+ return
+ fi
+
+ echo "** device must be in $1 config, but it's $CONFIG instead"
+ exit 1
+}
+
+
+echo "TESTING: $ARGS"
+
+while : true
+do
+ echo $(date)
+
+ for TYPE in $ARGS
+ do
+ # restore defaults
+ COUNT=5000
+ BUFLEN=2048
+
+ # FIXME automatically multiply COUNT by 10 when
+ # /sys/bus/usb/device/$THIS/speed == "480"
+
+# COUNT=50000
+
+ case $TYPE in
+ control)
+ # any device, in any configuration, can use this.
+ echo '** Control test cases:'
+
+ echo "test 9: ch9 postconfig"
+ do_test -t 9 -c 5000
+ echo "test 10: control queueing"
+ do_test -t 10 -c 5000
+
+ # this relies on some vendor-specific commands
+ echo "test 14: control writes"
+ do_test -t 14 -c 15000 -s 256 -v 1
+
+ echo "test 21: control writes, unaligned"
+ do_test -t 21 -c 100 -s 256 -v 1
+
+ ;;
+
+ out)
+ check_config sink-src
+ echo '** Host Write (OUT) test cases:'
+
+ echo "test 1: $COUNT transfers, same size"
+ do_test -t 1
+ echo "test 3: $COUNT transfers, variable/short size"
+ do_test -t 3 -v 421
+
+ COUNT=100
+ echo "test 17: $COUNT transfers, unaligned DMA map by core"
+ do_test -t 17
+
+ echo "test 19: $COUNT transfers, unaligned DMA map by usb_alloc_coherent"
+ do_test -t 19
+
+ COUNT=2000
+ echo "test 5: $COUNT scatterlists, same size entries"
+ do_test -t 5
+
+ # try to trigger short OUT processing bugs
+ echo "test 7a: $COUNT scatterlists, variable size/short entries"
+ do_test -t 7 -v 579
+ BUFLEN=4096
+ echo "test 7b: $COUNT scatterlists, variable size/bigger entries"
+ do_test -t 7 -v 41
+ BUFLEN=64
+ echo "test 7c: $COUNT scatterlists, variable size/micro entries"
+ do_test -t 7 -v 63
+ ;;
+
+ iso-out)
+ check_config sink-src
+ echo '** Host ISOCHRONOUS Write (OUT) test cases:'
+
+ # at peak iso transfer rates:
+ # - usb 2.0 high bandwidth, this is one frame.
+ # - usb 1.1, it's twenty-four frames.
+ BUFLEN=24500
+
+ COUNT=1000
+
+# COUNT=10000
+
+ echo "test 15: $COUNT transfers, same size"
+ # do_test -t 15 -g 3 -v 0
+ BUFLEN=32768
+ do_test -t 15 -g 8 -v 0
+
+ # FIXME it'd make sense to have an iso OUT test issuing
+ # short writes on more packets than the last one
+
+ COUNT=100
+ echo "test 22: $COUNT transfers, non aligned"
+ do_test -t 22 -g 8 -v 0
+
+ ;;
+
+ in)
+ check_config sink-src
+ echo '** Host Read (IN) test cases:'
+
+ # NOTE: these "variable size" reads are just multiples
+ # of 512 bytes, no EOVERFLOW testing is done yet
+
+ echo "test 2: $COUNT transfers, same size"
+ do_test -t 2
+ echo "test 4: $COUNT transfers, variable size"
+ do_test -t 4
+
+ COUNT=100
+ echo "test 18: $COUNT transfers, unaligned DMA map by core"
+ do_test -t 18
+
+ echo "test 20: $COUNT transfers, unaligned DMA map by usb_alloc_coherent"
+ do_test -t 20
+
+ COUNT=2000
+ echo "test 6: $COUNT scatterlists, same size entries"
+ do_test -t 6
+ echo "test 8: $COUNT scatterlists, variable size entries"
+ do_test -t 8
+ ;;
+
+ iso-in)
+ check_config sink-src
+ echo '** Host ISOCHRONOUS Read (IN) test cases:'
+
+ # at peak iso transfer rates:
+ # - usb 2.0 high bandwidth, this is one frame.
+ # - usb 1.1, it's twenty-four frames.
+ BUFLEN=24500
+
+ COUNT=1000
+
+# COUNT=10000
+
+ echo "test 16: $COUNT transfers, same size"
+ # do_test -t 16 -g 3 -v 0
+ BUFLEN=32768
+ do_test -t 16 -g 8 -v 0
+
+ # FIXME since iso expects faults, it'd make sense
+ # to have an iso IN test issuing short reads ...
+
+ COUNT=100
+ echo "test 23: $COUNT transfers, unaligned"
+ do_test -t 23 -g 8 -v 0
+
+ ;;
+
+ halt)
+ # NOTE: sometimes hardware doesn't cooperate well with halting
+ # endpoints from the host side. so long as mass-storage class
+ # firmware can halt them from the device, don't worry much if
+ # you can't make this test work on your device.
+ COUNT=2000
+ echo "test 13: $COUNT halt set/clear"
+ do_test -t 13
+ ;;
+
+ unlink)
+ COUNT=2000
+ echo "test 11: $COUNT read unlinks"
+ do_test -t 11
+
+ echo "test 12: $COUNT write unlinks"
+ do_test -t 12
+ ;;
+
+ loop)
+ # defaults need too much buffering for ez-usb devices
+ BUFLEN=2048
+ COUNT=32
+
+ # modprobe g_zero qlen=$COUNT buflen=$BUFLEN loopdefault
+ check_config loopback
+
+ # FIXME someone needs to write and merge a version of this
+
+ echo "write $COUNT buffers of $BUFLEN bytes, read them back"
+
+ echo "write $COUNT variable size buffers, read them back"
+
+ ;;
+
+ *)
+ echo "Don't understand test type $TYPE"
+ exit 1;
+ esac
+ echo ''
+ done
+done
diff --git a/tools/usb/p9_fwd.py b/tools/usb/p9_fwd.py
new file mode 100755
index 000000000..12c76cbb0
--- /dev/null
+++ b/tools/usb/p9_fwd.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+import argparse
+import errno
+import logging
+import socket
+import struct
+import time
+
+import usb.core
+import usb.util
+
+
+def path_from_usb_dev(dev):
+ """Takes a pyUSB device as argument and returns a string.
+ The string is a Path representation of the position of the USB device on the USB bus tree.
+
+ This path is used to find a USB device on the bus or all devices connected to a HUB.
+ The path is made up of the number of the USB controller followed be the ports of the HUB tree."""
+ if dev.port_numbers:
+ dev_path = ".".join(str(i) for i in dev.port_numbers)
+ return f"{dev.bus}-{dev_path}"
+ return ""
+
+
+HEXDUMP_FILTER = "".join(chr(x).isprintable() and chr(x) or "." for x in range(128)) + "." * 128
+
+
+class Forwarder:
+ @staticmethod
+ def _log_hexdump(data):
+ if not logging.root.isEnabledFor(logging.TRACE):
+ return
+ L = 16
+ for c in range(0, len(data), L):
+ chars = data[c : c + L]
+ dump = " ".join(f"{x:02x}" for x in chars)
+ printable = "".join(HEXDUMP_FILTER[x] for x in chars)
+ line = f"{c:08x} {dump:{L*3}s} |{printable:{L}s}|"
+ logging.root.log(logging.TRACE, "%s", line)
+
+ def __init__(self, server, vid, pid, path):
+ self.stats = {
+ "c2s packets": 0,
+ "c2s bytes": 0,
+ "s2c packets": 0,
+ "s2c bytes": 0,
+ }
+ self.stats_logged = time.monotonic()
+
+ def find_filter(dev):
+ dev_path = path_from_usb_dev(dev)
+ if path is not None:
+ return dev_path == path
+ return True
+
+ dev = usb.core.find(idVendor=vid, idProduct=pid, custom_match=find_filter)
+ if dev is None:
+ raise ValueError("Device not found")
+
+ logging.info(f"found device: {dev.bus}/{dev.address} located at {path_from_usb_dev(dev)}")
+
+ # dev.set_configuration() is not necessary since g_multi has only one
+ usb9pfs = None
+ # g_multi adds 9pfs as last interface
+ cfg = dev.get_active_configuration()
+ for intf in cfg:
+ # we have to detach the usb-storage driver from multi gadget since
+ # stall option could be set, which will lead to spontaneous port
+ # resets and our transfers will run dead
+ if intf.bInterfaceClass == 0x08:
+ if dev.is_kernel_driver_active(intf.bInterfaceNumber):
+ dev.detach_kernel_driver(intf.bInterfaceNumber)
+
+ if intf.bInterfaceClass == 0xFF and intf.bInterfaceSubClass == 0xFF and intf.bInterfaceProtocol == 0x09:
+ usb9pfs = intf
+ if usb9pfs is None:
+ raise ValueError("Interface not found")
+
+ logging.info(f"claiming interface:\n{usb9pfs}")
+ usb.util.claim_interface(dev, usb9pfs.bInterfaceNumber)
+ ep_out = usb.util.find_descriptor(
+ usb9pfs,
+ custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT,
+ )
+ assert ep_out is not None
+ ep_in = usb.util.find_descriptor(
+ usb9pfs,
+ custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN,
+ )
+ assert ep_in is not None
+ logging.info("interface claimed")
+
+ self.ep_out = ep_out
+ self.ep_in = ep_in
+ self.dev = dev
+
+ # create and connect socket
+ self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.s.connect(server)
+
+ logging.info("connected to server")
+
+ def c2s(self):
+ """forward a request from the USB client to the TCP server"""
+ data = None
+ while data is None:
+ try:
+ logging.log(logging.TRACE, "c2s: reading")
+ data = self.ep_in.read(self.ep_in.wMaxPacketSize)
+ except usb.core.USBTimeoutError:
+ logging.log(logging.TRACE, "c2s: reading timed out")
+ continue
+ except usb.core.USBError as e:
+ if e.errno == errno.EIO:
+ logging.debug("c2s: reading failed with %s, retrying", repr(e))
+ time.sleep(0.5)
+ continue
+ logging.error("c2s: reading failed with %s, aborting", repr(e))
+ raise
+ size = struct.unpack("<I", data[:4])[0]
+ while len(data) < size:
+ data += self.ep_in.read(size - len(data))
+ logging.log(logging.TRACE, "c2s: writing")
+ self._log_hexdump(data)
+ self.s.send(data)
+ logging.debug("c2s: forwarded %i bytes", size)
+ self.stats["c2s packets"] += 1
+ self.stats["c2s bytes"] += size
+
+ def s2c(self):
+ """forward a response from the TCP server to the USB client"""
+ logging.log(logging.TRACE, "s2c: reading")
+ data = self.s.recv(4)
+ size = struct.unpack("<I", data[:4])[0]
+ while len(data) < size:
+ data += self.s.recv(size - len(data))
+ logging.log(logging.TRACE, "s2c: writing")
+ self._log_hexdump(data)
+ while data:
+ written = self.ep_out.write(data)
+ assert written > 0
+ data = data[written:]
+ if size % self.ep_out.wMaxPacketSize == 0:
+ logging.log(logging.TRACE, "sending zero length packet")
+ self.ep_out.write(b"")
+ logging.debug("s2c: forwarded %i bytes", size)
+ self.stats["s2c packets"] += 1
+ self.stats["s2c bytes"] += size
+
+ def log_stats(self):
+ logging.info("statistics:")
+ for k, v in self.stats.items():
+ logging.info(f" {k+':':14s} {v}")
+
+ def log_stats_interval(self, interval=5):
+ if (time.monotonic() - self.stats_logged) < interval:
+ return
+
+ self.log_stats()
+ self.stats_logged = time.monotonic()
+
+
+def try_get_usb_str(dev, name):
+ try:
+ with open(f"/sys/bus/usb/devices/{dev.bus}-{dev.address}/{name}") as f:
+ return f.read().strip()
+ except FileNotFoundError:
+ return None
+
+
+def list_usb(args):
+ vid, pid = [int(x, 16) for x in args.id.split(":", 1)]
+
+ print("Bus | Addr | Manufacturer | Product | ID | Path")
+ print("--- | ---- | ---------------- | ---------------- | --------- | ----")
+ for dev in usb.core.find(find_all=True, idVendor=vid, idProduct=pid):
+ path = path_from_usb_dev(dev) or ""
+ manufacturer = try_get_usb_str(dev, "manufacturer") or "unknown"
+ product = try_get_usb_str(dev, "product") or "unknown"
+ print(
+ f"{dev.bus:3} | {dev.address:4} | {manufacturer:16} | {product:16} | {dev.idVendor:04x}:{dev.idProduct:04x} | {path:18}"
+ )
+
+
+def connect(args):
+ vid, pid = [int(x, 16) for x in args.id.split(":", 1)]
+
+ f = Forwarder(server=(args.server, args.port), vid=vid, pid=pid, path=args.path)
+
+ try:
+ while True:
+ f.c2s()
+ f.s2c()
+ f.log_stats_interval()
+ finally:
+ f.log_stats()
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Forward 9PFS requests from USB to TCP",
+ )
+
+ parser.add_argument("--id", type=str, default="1d6b:0109", help="vid:pid of target device")
+ parser.add_argument("--path", type=str, required=False, help="path of target device")
+ parser.add_argument("-v", "--verbose", action="count", default=0)
+
+ subparsers = parser.add_subparsers()
+ subparsers.required = True
+ subparsers.dest = "command"
+
+ parser_list = subparsers.add_parser("list", help="List all connected 9p gadgets")
+ parser_list.set_defaults(func=list_usb)
+
+ parser_connect = subparsers.add_parser(
+ "connect", help="Forward messages between the usb9pfs gadget and the 9p server"
+ )
+ parser_connect.set_defaults(func=connect)
+ connect_group = parser_connect.add_argument_group()
+ connect_group.required = True
+ parser_connect.add_argument("-s", "--server", type=str, default="127.0.0.1", help="server hostname")
+ parser_connect.add_argument("-p", "--port", type=int, default=564, help="server port")
+
+ args = parser.parse_args()
+
+ logging.TRACE = logging.DEBUG - 5
+ logging.addLevelName(logging.TRACE, "TRACE")
+
+ if args.verbose >= 2:
+ level = logging.TRACE
+ elif args.verbose:
+ level = logging.DEBUG
+ else:
+ level = logging.INFO
+ logging.basicConfig(level=level, format="%(asctime)-15s %(levelname)-8s %(message)s")
+
+ args.func(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
new file mode 100644
index 000000000..cbaa1b9fd
--- /dev/null
+++ b/tools/usb/testusb.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */
+
+/*
+ * Copyright (c) 2002 by David Brownell
+ * Copyright (c) 2010 by Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ */
+
+/*
+ * This program issues ioctls to perform the tests implemented by the
+ * kernel driver. It can generate a variety of transfer patterns; you
+ * should make sure to test both regular streaming and mixes of
+ * transfer sizes (including short transfers).
+ *
+ * For more information on how this can be used and on USB testing
+ * refer to <URL:http://www.linux-usb.org/usbtest/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ftw.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define TEST_CASES 30
+
+// FIXME make these public somewhere; usbdevfs.h?
+
+struct usbtest_param {
+ // inputs
+ unsigned test_num; /* 0..(TEST_CASES-1) */
+ unsigned iterations;
+ unsigned length;
+ unsigned vary;
+ unsigned sglen;
+
+ // outputs
+ struct timeval duration;
+};
+#define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+/* #include <linux/usb_ch9.h> */
+
+#define USB_DT_DEVICE 0x01
+#define USB_DT_INTERFACE 0x04
+
+#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+
+struct usb_device_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 bcdUSB;
+ __u8 bDeviceClass;
+ __u8 bDeviceSubClass;
+ __u8 bDeviceProtocol;
+ __u8 bMaxPacketSize0;
+ __u16 idVendor;
+ __u16 idProduct;
+ __u16 bcdDevice;
+ __u8 iManufacturer;
+ __u8 iProduct;
+ __u8 iSerialNumber;
+ __u8 bNumConfigurations;
+} __attribute__ ((packed));
+
+struct usb_interface_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bInterfaceNumber;
+ __u8 bAlternateSetting;
+ __u8 bNumEndpoints;
+ __u8 bInterfaceClass;
+ __u8 bInterfaceSubClass;
+ __u8 bInterfaceProtocol;
+ __u8 iInterface;
+} __attribute__ ((packed));
+
+enum usb_device_speed {
+ USB_SPEED_UNKNOWN = 0, /* enumerating */
+ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
+ USB_SPEED_HIGH, /* usb 2.0 */
+ USB_SPEED_WIRELESS, /* wireless (usb 2.5) */
+ USB_SPEED_SUPER, /* usb 3.0 */
+ USB_SPEED_SUPER_PLUS, /* usb 3.1 */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static char *speed (enum usb_device_speed s)
+{
+ switch (s) {
+ case USB_SPEED_UNKNOWN: return "unknown";
+ case USB_SPEED_LOW: return "low";
+ case USB_SPEED_FULL: return "full";
+ case USB_SPEED_HIGH: return "high";
+ case USB_SPEED_WIRELESS: return "wireless";
+ case USB_SPEED_SUPER: return "super";
+ case USB_SPEED_SUPER_PLUS: return "super-plus";
+ default: return "??";
+ }
+}
+
+struct testdev {
+ struct testdev *next;
+ char *name;
+ pthread_t thread;
+ enum usb_device_speed speed;
+ unsigned ifnum : 8;
+ unsigned forever : 1;
+ int test;
+
+ struct usbtest_param param;
+};
+static struct testdev *testdevs;
+
+static int testdev_ffs_ifnum(FILE *fd)
+{
+ union {
+ char buf[255];
+ struct usb_interface_descriptor intf;
+ } u;
+
+ for (;;) {
+ if (fread(u.buf, 1, 1, fd) != 1)
+ return -1;
+ if (fread(u.buf + 1, (unsigned char)u.buf[0] - 1, 1, fd) != 1)
+ return -1;
+
+ if (u.intf.bLength == sizeof u.intf
+ && u.intf.bDescriptorType == USB_DT_INTERFACE
+ && u.intf.bNumEndpoints == 2
+ && u.intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC
+ && u.intf.bInterfaceSubClass == 0
+ && u.intf.bInterfaceProtocol == 0)
+ return (unsigned char)u.intf.bInterfaceNumber;
+ }
+}
+
+static int testdev_ifnum(FILE *fd)
+{
+ struct usb_device_descriptor dev;
+
+ if (fread(&dev, sizeof dev, 1, fd) != 1)
+ return -1;
+
+ if (dev.bLength != sizeof dev || dev.bDescriptorType != USB_DT_DEVICE)
+ return -1;
+
+ /* FX2 with (tweaked) bulksrc firmware */
+ if (dev.idVendor == 0x0547 && dev.idProduct == 0x1002)
+ return 0;
+
+ /*----------------------------------------------------*/
+
+ /* devices that start up using the EZ-USB default device and
+ * which we can use after loading simple firmware. hotplug
+ * can fxload it, and then run this test driver.
+ *
+ * we return false positives in two cases:
+ * - the device has a "real" driver (maybe usb-serial) that
+ * renumerates. the device should vanish quickly.
+ * - the device doesn't have the test firmware installed.
+ */
+
+ /* generic EZ-USB FX controller */
+ if (dev.idVendor == 0x0547 && dev.idProduct == 0x2235)
+ return 0;
+
+ /* generic EZ-USB FX2 controller */
+ if (dev.idVendor == 0x04b4 && dev.idProduct == 0x8613)
+ return 0;
+
+ /* CY3671 development board with EZ-USB FX */
+ if (dev.idVendor == 0x0547 && dev.idProduct == 0x0080)
+ return 0;
+
+ /* Keyspan 19Qi uses an21xx (original EZ-USB) */
+ if (dev.idVendor == 0x06cd && dev.idProduct == 0x010b)
+ return 0;
+
+ /*----------------------------------------------------*/
+
+ /* "gadget zero", Linux-USB test software */
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a0)
+ return 0;
+
+ /* user mode subset of that */
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a4)
+ return testdev_ffs_ifnum(fd);
+ /* return 0; */
+
+ /* iso version of usermode code */
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4a3)
+ return 0;
+
+ /* some GPL'd test firmware uses these IDs */
+
+ if (dev.idVendor == 0xfff0 && dev.idProduct == 0xfff0)
+ return 0;
+
+ /*----------------------------------------------------*/
+
+ /* iBOT2 high speed webcam */
+ if (dev.idVendor == 0x0b62 && dev.idProduct == 0x0059)
+ return 0;
+
+ /*----------------------------------------------------*/
+
+ /* the FunctionFS gadget can have the source/sink interface
+ * anywhere. We look for an interface descriptor that match
+ * what we expect. We ignore configuratiens thou. */
+
+ if (dev.idVendor == 0x0525 && dev.idProduct == 0xa4ac
+ && (dev.bDeviceClass == USB_CLASS_PER_INTERFACE
+ || dev.bDeviceClass == USB_CLASS_VENDOR_SPEC))
+ return testdev_ffs_ifnum(fd);
+
+ return -1;
+}
+
+static int find_testdev(const char *name, const struct stat *sb, int flag)
+{
+ FILE *fd;
+ int ifnum;
+ struct testdev *entry;
+
+ (void)sb; /* unused */
+
+ if (flag != FTW_F)
+ return 0;
+
+ fd = fopen(name, "rb");
+ if (!fd) {
+ perror(name);
+ return 0;
+ }
+
+ ifnum = testdev_ifnum(fd);
+ fclose(fd);
+ if (ifnum < 0)
+ return 0;
+
+ entry = calloc(1, sizeof *entry);
+ if (!entry)
+ goto nomem;
+
+ entry->name = strdup(name);
+ if (!entry->name) {
+ free(entry);
+nomem:
+ perror("malloc");
+ return 0;
+ }
+
+ entry->ifnum = ifnum;
+ entry->next = testdevs;
+ testdevs = entry;
+ return 0;
+}
+
+static int
+usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
+{
+ struct usbdevfs_ioctl wrapper;
+
+ wrapper.ifno = ifno;
+ wrapper.ioctl_code = request;
+ wrapper.data = param;
+
+ return ioctl (fd, USBDEVFS_IOCTL, &wrapper);
+}
+
+static void *handle_testdev (void *arg)
+{
+ struct testdev *dev = arg;
+ int fd, i;
+ int status;
+
+ if ((fd = open (dev->name, O_RDWR)) < 0) {
+ perror ("can't open dev file r/w");
+ return 0;
+ }
+
+ status = ioctl(fd, USBDEVFS_GET_SPEED, NULL);
+ if (status < 0)
+ fprintf(stderr, "USBDEVFS_GET_SPEED failed %d\n", status);
+ else
+ dev->speed = status;
+ fprintf(stderr, "%s speed\t%s\t%u\n",
+ speed(dev->speed), dev->name, dev->ifnum);
+
+restart:
+ for (i = 0; i < TEST_CASES; i++) {
+ if (dev->test != -1 && dev->test != i)
+ continue;
+ dev->param.test_num = i;
+
+ status = usbdev_ioctl (fd, dev->ifnum,
+ USBTEST_REQUEST, &dev->param);
+ if (status < 0 && errno == EOPNOTSUPP)
+ continue;
+
+ /* FIXME need a "syslog it" option for background testing */
+
+ /* NOTE: each thread emits complete lines; no fragments! */
+ if (status < 0) {
+ char buf [80];
+ int err = errno;
+
+ if (strerror_r (errno, buf, sizeof buf)) {
+ snprintf (buf, sizeof buf, "error %d", err);
+ errno = err;
+ }
+ printf ("%s test %d --> %d (%s)\n",
+ dev->name, i, errno, buf);
+ } else
+ printf ("%s test %d, %4d.%.06d secs\n", dev->name, i,
+ (int) dev->param.duration.tv_sec,
+ (int) dev->param.duration.tv_usec);
+
+ fflush (stdout);
+ }
+ if (dev->forever)
+ goto restart;
+
+ close (fd);
+ return arg;
+}
+
+static const char *usb_dir_find(void)
+{
+ static char udev_usb_path[] = "/dev/bus/usb";
+
+ if (access(udev_usb_path, F_OK) == 0)
+ return udev_usb_path;
+
+ return NULL;
+}
+
+static int parse_num(unsigned *num, const char *str)
+{
+ unsigned long val;
+ char *end;
+
+ errno = 0;
+ val = strtoul(str, &end, 0);
+ if (errno || *end || val > UINT_MAX)
+ return -1;
+ *num = val;
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+
+ int c;
+ struct testdev *entry;
+ char *device;
+ const char *usb_dir = NULL;
+ int all = 0, forever = 0, not = 0;
+ int test = -1 /* all */;
+ struct usbtest_param param;
+
+ /* pick defaults that works with all speeds, without short packets.
+ *
+ * Best per-frame data rates:
+ * super speed,bulk 1024 * 16 * 8 = 131072
+ * interrupt 1024 * 3 * 8 = 24576
+ * high speed, bulk 512 * 13 * 8 = 53248
+ * interrupt 1024 * 3 * 8 = 24576
+ * full speed, bulk/intr 64 * 19 = 1216
+ * interrupt 64 * 1 = 64
+ * low speed, interrupt 8 * 1 = 8
+ */
+ param.iterations = 1000;
+ param.length = 1024;
+ param.vary = 1024;
+ param.sglen = 32;
+
+ /* for easy use when hotplugging */
+ device = getenv ("DEVICE");
+
+ while ((c = getopt (argc, argv, "D:aA:c:g:hlns:t:v:")) != EOF)
+ switch (c) {
+ case 'D': /* device, if only one */
+ device = optarg;
+ continue;
+ case 'A': /* use all devices with specified USB dir */
+ usb_dir = optarg;
+ /* FALL THROUGH */
+ case 'a': /* use all devices */
+ device = NULL;
+ all = 1;
+ continue;
+ case 'c': /* count iterations */
+ if (parse_num(&param.iterations, optarg))
+ goto usage;
+ continue;
+ case 'g': /* scatter/gather entries */
+ if (parse_num(&param.sglen, optarg))
+ goto usage;
+ continue;
+ case 'l': /* loop forever */
+ forever = 1;
+ continue;
+ case 'n': /* no test running! */
+ not = 1;
+ continue;
+ case 's': /* size of packet */
+ if (parse_num(&param.length, optarg))
+ goto usage;
+ continue;
+ case 't': /* run just one test */
+ test = atoi (optarg);
+ if (test < 0)
+ goto usage;
+ continue;
+ case 'v': /* vary packet size by ... */
+ if (parse_num(&param.vary, optarg))
+ goto usage;
+ continue;
+ case '?':
+ case 'h':
+ default:
+usage:
+ fprintf (stderr,
+ "usage: %s [options]\n"
+ "Options:\n"
+ "\t-D dev only test specific device\n"
+ "\t-A usb-dir\n"
+ "\t-a test all recognized devices\n"
+ "\t-l loop forever(for stress test)\n"
+ "\t-t testnum only run specified case\n"
+ "\t-n no test running, show devices to be tested\n"
+ "Case arguments:\n"
+ "\t-c iterations default 1000\n"
+ "\t-s transfer length default 1024\n"
+ "\t-g sglen default 32\n"
+ "\t-v vary default 1024\n",
+ argv[0]);
+ return 1;
+ }
+ if (optind != argc)
+ goto usage;
+ if (!all && !device) {
+ fprintf (stderr, "must specify '-a' or '-D dev', "
+ "or DEVICE=/dev/bus/usb/BBB/DDD in env\n");
+ goto usage;
+ }
+
+ /* Find usb device subdirectory */
+ if (!usb_dir) {
+ usb_dir = usb_dir_find();
+ if (!usb_dir) {
+ fputs ("USB device files are missing\n", stderr);
+ return -1;
+ }
+ }
+
+ /* collect and list the test devices */
+ if (ftw (usb_dir, find_testdev, 3) != 0) {
+ fputs ("ftw failed; are USB device files missing?\n", stderr);
+ return -1;
+ }
+
+ /* quit, run single test, or create test threads */
+ if (!testdevs && !device) {
+ fputs ("no test devices recognized\n", stderr);
+ return -1;
+ }
+ if (not)
+ return 0;
+ if (testdevs && !testdevs->next && !device)
+ device = testdevs->name;
+ for (entry = testdevs; entry; entry = entry->next) {
+ int status;
+
+ entry->param = param;
+ entry->forever = forever;
+ entry->test = test;
+
+ if (device) {
+ if (strcmp (entry->name, device))
+ continue;
+ return handle_testdev (entry) != entry;
+ }
+ status = pthread_create (&entry->thread, 0, handle_testdev, entry);
+ if (status)
+ perror ("pthread_create");
+ }
+ if (device) {
+ struct testdev dev;
+
+ /* kernel can recognize test devices we don't */
+ fprintf (stderr, "%s: %s may see only control tests\n",
+ argv [0], device);
+
+ memset (&dev, 0, sizeof dev);
+ dev.name = device;
+ dev.param = param;
+ dev.forever = forever;
+ dev.test = test;
+ return handle_testdev (&dev) != &dev;
+ }
+
+ /* wait for tests to complete */
+ for (entry = testdevs; entry; entry = entry->next) {
+ void *retval;
+
+ if (pthread_join (entry->thread, &retval))
+ perror ("pthread_join");
+ /* testing errors discarded! */
+ }
+
+ return 0;
+}
diff --git a/tools/usb/usbip/.gitignore b/tools/usb/usbip/.gitignore
new file mode 100644
index 000000000..597361a96
--- /dev/null
+++ b/tools/usb/usbip/.gitignore
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-only
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libsrc/Makefile
+libsrc/Makefile.in
+libtool
+ltmain.sh
+missing
+src/Makefile
+src/Makefile.in
+stamp-h1
+libsrc/libusbip.la
+libsrc/libusbip_la-names.lo
+libsrc/libusbip_la-sysfs_utils.lo
+libsrc/libusbip_la-usbip_common.lo
+libsrc/libusbip_la-usbip_device_driver.lo
+libsrc/libusbip_la-usbip_host_common.lo
+libsrc/libusbip_la-usbip_host_driver.lo
+libsrc/libusbip_la-vhci_driver.lo
+src/usbip
+src/usbipd
diff --git a/tools/usb/usbip/AUTHORS b/tools/usb/usbip/AUTHORS
new file mode 100644
index 000000000..a27ea8d03
--- /dev/null
+++ b/tools/usb/usbip/AUTHORS
@@ -0,0 +1,3 @@
+Takahiro Hirofuchi
+Robert Leibl
+matt mooney <mfm@muteddisk.com>
diff --git a/tools/usb/usbip/COPYING b/tools/usb/usbip/COPYING
new file mode 100644
index 000000000..c5611e48a
--- /dev/null
+++ b/tools/usb/usbip/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/tools/usb/usbip/INSTALL b/tools/usb/usbip/INSTALL
new file mode 100644
index 000000000..d3c5b40a9
--- /dev/null
+++ b/tools/usb/usbip/INSTALL
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/tools/usb/usbip/Makefile.am b/tools/usb/usbip/Makefile.am
new file mode 100644
index 000000000..5961e9c18
--- /dev/null
+++ b/tools/usb/usbip/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+SUBDIRS := libsrc src
+includedir = @includedir@/usbip
+include_HEADERS := $(addprefix libsrc/, \
+ usbip_common.h vhci_driver.h usbip_host_driver.h \
+ list.h sysfs_utils.h usbip_host_common.h)
+
+dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8)
diff --git a/tools/usb/usbip/README b/tools/usb/usbip/README
new file mode 100644
index 000000000..11971538f
--- /dev/null
+++ b/tools/usb/usbip/README
@@ -0,0 +1,255 @@
+#
+# README for usbip-utils
+#
+# Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+# 2005-2008 Takahiro Hirofuchi
+
+[Overview]
+USB/IP protocol allows to pass USB device from server to client over the
+network. Server is a machine which provides (shares) a USB device. Client is
+a machine which uses USB device provided by server over the network.
+The USB device may be either physical device connected to a server or
+software entity created on a server using USB gadget subsystem.
+Whole project consists of four parts:
+
+ - usbip-vhci
+ A client side kernel module which provides a virtual USB Host Controller
+ and allows to import a USB device from a remote machine.
+
+ - usbip-host (stub driver)
+ A server side module which provides a USB device driver which can be
+ bound to a physical USB device to make it exportable.
+
+ - usbip-vudc
+ A server side module which provides a virtual USB Device Controller and allows
+ to export a USB device created using USB Gadget Subsystem.
+
+ - usbip-utils
+ A set of userspace tools used to handle connection and management.
+ Used on both sides.
+
+[Requirements]
+ - USB/IP device drivers
+ Found in the drivers/usb/usbip/ directory of the Linux kernel tree.
+
+ - libudev >= 2.0
+ libudev library
+
+ - libwrap0-dev
+ tcp wrapper library
+
+ - gcc >= 4.0
+
+ - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
+
+[Optional]
+ - hwdata
+ Contains USB device identification data.
+
+
+[Install]
+ 0. Generate configuration scripts.
+ $ ./autogen.sh
+
+ 1. Compile & install the userspace utilities.
+ $ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
+ $ make install
+
+ 2. Compile & install USB/IP drivers.
+
+
+[Usage]
+On a server side there are two entities which can be shared.
+First of them is physical usb device connected to the machine.
+To make it available below steps should be executed:
+
+ server:# (Physically attach your USB device.)
+
+ server:# insmod usbip-core.ko
+ server:# insmod usbip-host.ko
+
+ server:# usbipd -D
+ - Start usbip daemon.
+
+ server:# usbip list -l
+ - List driver assignments for USB devices.
+
+ server:# usbip bind --busid 1-2
+ - Bind usbip-host.ko to the device with busid 1-2.
+ - The USB device 1-2 is now exportable to other hosts!
+ - Use `usbip unbind --busid 1-2' to stop exporting the device.
+
+Second of shareable entities is USB Gadget created using USB Gadget Subsystem
+on a server machine. To make it available below steps should be executed:
+
+ server:# (Create your USB gadget)
+ - Currently the most preferable way of creating a new USB gadget
+ is ConfigFS Composite Gadget. Please refer to its documentation
+ for details.
+ - See vudc_server_example.sh for a short example of USB gadget creation
+
+ server:# insmod usbip-core.ko
+ server:# insmod usbip-vudc.ko
+ - To create more than one instance of vudc use num module param
+
+ server:# (Bind gadget to one of available vudc)
+ - Assign your new gadget to USB/IP UDC
+ - Using ConfigFS interface you may do this simply by:
+ server:# cd /sys/kernel/config/usb_gadget/<gadget_name>
+ server:# echo "usbip-vudc.0" > UDC
+
+ server:# usbipd -D --device
+ - Start usbip daemon.
+
+To attach new device to client machine below commands should be used:
+
+ client:# insmod usbip-core.ko
+ client:# insmod vhci-hcd.ko
+
+ client:# usbip list --remote <host>
+ - List exported USB devices on the <host>.
+
+ client:# usbip attach --remote <host> --busid 1-2
+ - Connect the remote USB device.
+ - When using vudc on a server side busid is really vudc instance name.
+ For example: usbip-vudc.0
+
+ client:# usbip port
+ - Show virtual port status.
+
+ client:# usbip detach --port <port>
+ - Detach the USB device.
+
+
+[Example]
+---------------------------
+ SERVER SIDE
+---------------------------
+Physically attach your USB devices to this host.
+
+ trois:# insmod path/to/usbip-core.ko
+ trois:# insmod path/to/usbip-host.ko
+ trois:# usbipd -D
+
+In another terminal, let's look up what USB devices are physically
+attached to this host.
+
+ trois:# usbip list -l
+ Local USB devices
+ =================
+ - busid 1-1 (05a9:a511)
+ 1-1:1.0
+
+ - busid 3-2 (0711:0902)
+ 3-2:1.0
+
+ - busid 3-3.1 (08bb:2702)
+ 3-3.1:1.0
+ 3-3.1:1.1
+
+ - busid 3-3.2 (04bb:0206)
+ 3-3.2:1.0
+
+ - busid 3-3 (0409:0058)
+ 3-3:1.0
+
+ - busid 4-1 (046d:08b2)
+ 4-1:1.0
+ 4-1:1.1
+ 4-1:1.2
+
+ - busid 5-2 (058f:9254)
+ 5-2:1.0
+
+A USB storage device of busid 3-3.2 is now bound to the usb-storage
+driver. To export this device, we first mark the device as
+"exportable"; the device is bound to the usbip-host driver. Please
+remember you can not export a USB hub.
+
+Mark the device of busid 3-3.2 as exportable:
+
+ trois:# usbip --debug bind --busid 3-3.2
+ ...
+ usbip debug: usbip_bind.c:162:[unbind_other] 3-3.2:1.0 -> usb-storage
+ ...
+ bind device on busid 3-3.2: complete
+
+ trois:# usbip list -l
+ Local USB devices
+ =================
+ ...
+
+ - busid 3-3.2 (04bb:0206)
+ 3-3.2:1.0
+ ...
+
+---------------------------
+ CLIENT SIDE
+---------------------------
+First, let's list available remote devices that are marked as
+exportable on the host.
+
+ deux:# insmod path/to/usbip-core.ko
+ deux:# insmod path/to/vhci-hcd.ko
+
+ deux:# usbip list --remote 10.0.0.3
+ Exportable USB devices
+ ======================
+ - 10.0.0.3
+ 1-1: Prolific Technology, Inc. : unknown product (067b:3507)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Mass Storage / SCSI / Bulk (Zip) (08/06/50)
+
+ 1-2.2.1: Apple Computer, Inc. : unknown product (05ac:0203)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Human Interface Devices / Boot Interface Subclass / Keyboard (03/01/01)
+
+ 1-2.2.3: OmniVision Technologies, Inc. : OV511+ WebCam (05a9:a511)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.3
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00)
+
+ 3-1: Logitech, Inc. : QuickCam Pro 4000 (046d:08b2)
+ : /sys/devices/pci0000:00/0000:00:1e.0/0000:02:0a.0/usb3/3-1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Data / unknown subclass / unknown protocol (0a/ff/00)
+ : 1 - Audio / Control Device / unknown protocol (01/01/00)
+ : 2 - Audio / Streaming / unknown protocol (01/02/00)
+
+Attach a remote USB device:
+
+ deux:# usbip attach --remote 10.0.0.3 --busid 1-1
+ port 0 attached
+
+Show the devices attached to this client:
+
+ deux:# usbip port
+ Port 00: <Port in Use> at Full Speed(12Mbps)
+ Prolific Technology, Inc. : unknown product (067b:3507)
+ 6-1 -> usbip://10.0.0.3:3240/1-1 (remote bus/dev 001/004)
+ 6-1:1.0 used by usb-storage
+ /sys/class/scsi_device/0:0:0:0/device
+ /sys/class/scsi_host/host0/device
+ /sys/block/sda/device
+
+Detach the imported device:
+
+ deux:# usbip detach --port 0
+ port 0 detached
+
+
+[Checklist]
+ - usbip-host.ko must be bound to the target device.
+ - See /sys/kernel/debug/usb/devices and find "Driver=..." lines of the device.
+ - Target USB gadget must be bound to vudc
+ (using USB gadget susbsys, not usbip bind command)
+ - Shutdown firewall.
+ - usbip now uses TCP port 3240.
+ - Disable SELinux.
+ - Check the kernel and daemon messages.
+
+
+[Contact]
+ Mailing List: linux-usb@vger.kernel.org
diff --git a/tools/usb/usbip/autogen.sh b/tools/usb/usbip/autogen.sh
new file mode 100755
index 000000000..e1112d3fc
--- /dev/null
+++ b/tools/usb/usbip/autogen.sh
@@ -0,0 +1,9 @@
+#!/bin/sh -x
+
+#aclocal
+#autoheader
+#libtoolize --copy --force
+#automake-1.9 -acf
+#autoconf
+
+autoreconf -i -f -v
diff --git a/tools/usb/usbip/cleanup.sh b/tools/usb/usbip/cleanup.sh
new file mode 100755
index 000000000..8028c3a5c
--- /dev/null
+++ b/tools/usb/usbip/cleanup.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+if [ -r Makefile ]; then
+ make distclean
+fi
+
+FILES="aclocal.m4 autom4te.cache compile config.guess config.h.in config.log \
+ config.status config.sub configure cscope.out depcomp install-sh \
+ libsrc/Makefile libsrc/Makefile.in libtool ltmain.sh Makefile \
+ Makefile.in missing src/Makefile src/Makefile.in"
+
+rm -vRf $FILES
diff --git a/tools/usb/usbip/configure.ac b/tools/usb/usbip/configure.ac
new file mode 100644
index 000000000..8debf934f
--- /dev/null
+++ b/tools/usb/usbip/configure.ac
@@ -0,0 +1,111 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT([usbip-utils], [2.0], [linux-usb@vger.kernel.org])
+AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
+
+CURRENT=0
+REVISION=1
+AGE=0
+AC_SUBST([LIBUSBIP_VERSION], [$CURRENT:$REVISION:$AGE], [library version])
+
+AC_CONFIG_SRCDIR([src/usbipd.c])
+AC_CONFIG_HEADERS([config.h])
+
+AM_INIT_AUTOMAKE([foreign])
+LT_INIT
+
+# Silent build for automake >= 1.11
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_SUBST([EXTRA_CFLAGS], ["-Wall -Werror -Wextra -std=gnu99"])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h dnl
+ string.h sys/socket.h syslog.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT32_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([memset mkdir regcomp socket strchr strerror strstr dnl
+ strtoul])
+
+AC_CHECK_HEADER([libudev.h],
+ [AC_CHECK_LIB([udev], [udev_new],
+ [LIBS="$LIBS -ludev"],
+ [AC_MSG_ERROR([Missing udev library!])])],
+ [AC_MSG_ERROR([Missing /usr/include/libudev.h])])
+
+# Checks for libwrap library.
+AC_MSG_CHECKING([whether to use the libwrap (TCP wrappers) library])
+AC_ARG_WITH([tcp-wrappers],
+ [AS_HELP_STRING([--with-tcp-wrappers],
+ [use the libwrap (TCP wrappers) library])],
+ dnl [ACTION-IF-GIVEN]
+ [if test "$withval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ AC_MSG_CHECKING([for hosts_access in -lwrap])
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
+ AC_TRY_LINK(
+ [int hosts_access(); int allow_severity, deny_severity;],
+ [hosts_access()],
+ [AC_MSG_RESULT([yes]);
+ AC_DEFINE([HAVE_LIBWRAP], [1],
+ [use tcp wrapper]) wrap_LIB="-lwrap"],
+ [AC_MSG_RESULT([not found]); exit 1])
+ else
+ AC_MSG_RESULT([no]);
+ fi],
+ dnl [ACTION-IF-NOT-GIVEN]
+ [AC_MSG_RESULT([(default)])
+ AC_MSG_CHECKING([for hosts_access in -lwrap])
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
+ AC_TRY_LINK(
+ [int hosts_access(); int allow_severity, deny_severity;],
+ [hosts_access()],
+ [AC_MSG_RESULT([yes]);
+ AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
+ [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
+
+# Sets directory containing usb.ids.
+AC_ARG_WITH([usbids-dir],
+ [AS_HELP_STRING([--with-usbids-dir=DIR],
+ [where usb.ids is found (default /usr/share/hwdata/)])],
+ [USBIDS_DIR=$withval], [USBIDS_DIR="/usr/share/hwdata/"])
+AC_SUBST([USBIDS_DIR])
+
+# use _FORTIFY_SOURCE
+AC_MSG_CHECKING([whether to use fortify])
+AC_ARG_WITH([fortify],
+ [AS_HELP_STRING([--with-fortify],
+ [use _FORTIFY_SOURCE=2 option when compiling)])],
+ dnl [ACTION-IF-GIVEN]
+ [if test "$withval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2 -O"
+ else
+ AC_MSG_RESULT([no])
+ CFLAGS="$CFLAGS -U_FORTIFY_SOURCE"
+ fi
+ ],
+ dnl [ACTION-IF-NOT-GIVEN]
+ [AC_MSG_RESULT([default])])
+
+AC_CONFIG_FILES([Makefile libsrc/Makefile src/Makefile])
+AC_OUTPUT
diff --git a/tools/usb/usbip/doc/usbip.8 b/tools/usb/usbip/doc/usbip.8
new file mode 100644
index 000000000..1f26e4a00
--- /dev/null
+++ b/tools/usb/usbip/doc/usbip.8
@@ -0,0 +1,137 @@
+.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
+.SH NAME
+usbip \- manage USB/IP devices
+.SH SYNOPSIS
+.B usbip
+[\fIoptions\fR] <\fIcommand\fR> <\fIargs\fR>
+
+.SH DESCRIPTION
+On a USB/IP server, devices can be listed, bound, and unbound using
+this program. On a USB/IP client, devices exported by USB/IP servers
+can be listed, attached and detached.
+
+.SH OPTIONS
+.HP
+\fB\-\-debug\fR
+.IP
+Print debugging information.
+.PP
+
+.HP
+\fB\-\-log\fR
+.IP
+Log to syslog.
+.PP
+
+.HP
+\fB\-\-tcp-port PORT\fR
+.IP
+Connect to PORT on remote host (used for attach and list --remote).
+.PP
+
+.SH COMMANDS
+.HP
+\fBversion\fR
+.IP
+Show version and exit.
+.PP
+
+.HP
+\fBhelp\fR [\fIcommand\fR]
+.IP
+Print the program help message, or help on a specific command, and
+then exit.
+.PP
+
+.HP
+\fBattach\fR \-\-remote=<\fIhost\fR> \-\-busid=<\fIbus_id\fR>
+.IP
+Attach a remote USB device.
+.PP
+
+.HP
+\fBattach\fR \-\-remote=<\fIhost\fR> \-\-device=<\fIdev_id\fR>
+.IP
+Attach a remote USB gadget.
+Only used when the remote usbipd is in device mode.
+.PP
+
+.HP
+\fBdetach\fR \-\-port=<\fIport\fR>
+.IP
+Detach an imported USB device/gadget.
+.PP
+
+.HP
+\fBbind\fR \-\-busid=<\fIbusid\fR>
+.IP
+Make a device exportable.
+.PP
+
+.HP
+\fBunbind\fR \-\-busid=<\fIbusid\fR>
+.IP
+Stop exporting a device so it can be used by a local driver.
+.PP
+
+.HP
+\fBlist\fR \-\-remote=<\fIhost\fR>
+.IP
+List USB devices exported by a remote host.
+.PP
+
+.HP
+\fBlist\fR \-\-device
+.IP
+List USB gadgets of local usbip-vudc.
+Only used when the local usbipd is in device mode.
+Note that this can not list usbip-vudc USB gadgets of the remote device mode usbipd.
+.PP
+
+.HP
+\fBlist\fR \-\-local
+.IP
+List local USB devices.
+.PP
+
+.HP
+\fBport\fR
+.IP
+List imported devices/gadgets.
+.PP
+
+
+.SH EXAMPLES
+
+ client:# usbip list --remote=server
+ - List devices exported by remote server.
+
+ client:# modprobe vhci-hcd
+
+ client:# usbip attach --remote=server --busid=1-2
+ - Connect the remote USB device.
+
+ client:# usbip port
+ - List imported devices/gadgets.
+
+ client:# usbip detach --port=0
+ - Detach the usb device.
+
+The following example shows the usage of device mode
+
+ server:# usbip list --device
+ - List gadgets exported by local usbipd server.
+
+ client:# modprobe vhci-hcd
+
+ client:# usbip attach --remote=server --device=usbip-vudc.0
+ - Connect the remote USB gadget.
+
+ client:# usbip port
+ - List imported devices/gadgets.
+
+ client:# usbip detach --port=0
+ - Detach the usb gadget.
+
+.SH "SEE ALSO"
+\fBusbipd\fP\fB(8)\fB\fP
diff --git a/tools/usb/usbip/doc/usbipd.8 b/tools/usb/usbip/doc/usbipd.8
new file mode 100644
index 000000000..d974394f8
--- /dev/null
+++ b/tools/usb/usbip/doc/usbipd.8
@@ -0,0 +1,117 @@
+.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
+.SH NAME
+usbipd \- USB/IP server daemon
+.SH SYNOPSIS
+.B usbipd
+[\fIoptions\fR]
+
+.SH DESCRIPTION
+.B usbipd
+provides USB/IP clients access to exported USB devices.
+
+Devices have to explicitly be exported using
+.B usbip bind
+before usbipd makes them available to other hosts.
+
+The daemon accepts connections from USB/IP clients
+on TCP port 3240 by default.
+
+.SH OPTIONS
+.HP
+\fB\-4\fR, \fB\-\-ipv4\fR
+.IP
+Bind to IPv4. Default is both.
+.PP
+
+.HP
+\fB\-6\fR, \fB\-\-ipv6\fR
+.IP
+Bind to IPv6. Default is both.
+.PP
+
+.HP
+\fB\-e\fR, \fB\-\-device\fR
+.IP
+Run in device mode. Rather than drive an attached device, create a virtual UDC to bind gadgets to.
+.PP
+
+.HP
+\fB\-D\fR, \fB\-\-daemon\fR
+.IP
+Run as a daemon process.
+.PP
+
+.HP
+\fB\-d\fR, \fB\-\-debug\fR
+.IP
+Print debugging information.
+.PP
+
+.HP
+\fB\-PFILE\fR, \fB\-\-pid FILE\fR
+.IP
+Write process id to FILE.
+.br
+If no FILE specified, use /var/run/usbipd.pid
+.PP
+
+\fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR
+.IP
+Listen on TCP/IP port PORT.
+.PP
+
+\fB\-h\fR, \fB\-\-help\fR
+.IP
+Print the program help message and exit.
+.PP
+
+.HP
+\fB\-v\fR, \fB\-\-version\fR
+.IP
+Show version.
+.PP
+
+.SH LIMITATIONS
+
+.B usbipd
+offers no authentication or authorization for USB/IP. Any
+USB/IP client can connect and use exported devices.
+
+.SH EXAMPLES
+
+ server:# modprobe usbip-host
+
+ server:# usbipd -D
+ - Start usbip daemon.
+
+ server:# usbip list --local
+ - List driver assignments for usb devices.
+
+ server:# usbip bind --busid=1-2
+ - Bind usbip-host.ko to the device of busid 1-2.
+ - A usb device 1-2 is now exportable to other hosts!
+ - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally.
+
+The following example shows the usage of device mode
+
+ server:# modprobe usbip-vudc
+ - Use /sys/class/udc/ interface.
+ - usbip-host is independent of this module.
+
+ server:# usbipd -e -D
+ - Start usbip daemon in device mode.
+
+ server:# modprobe g_mass_storage file=/tmp/tmp.img
+ - Bind a gadget to usbip-vudc.
+ - in this example, a mass storage gadget is bound.
+
+ server:# usbip list --device
+ - List gadgets exported by local usbipd server.
+
+ server:# modprobe -r g_mass_storage
+ - Unbind a gadget from usbip-vudc.
+ - in this example, the previous mass storage gadget is unbound.
+
+.SH "SEE ALSO"
+\fBusbip\fP\fB(8)\fB\fP
+
diff --git a/tools/usb/usbip/libsrc/Makefile.am b/tools/usb/usbip/libsrc/Makefile.am
new file mode 100644
index 000000000..dabd2c91d
--- /dev/null
+++ b/tools/usb/usbip/libsrc/Makefile.am
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
+libusbip_la_CFLAGS = @EXTRA_CFLAGS@
+libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@
+
+lib_LTLIBRARIES := libusbip.la
+libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \
+ usbip_device_driver.c usbip_device_driver.h \
+ usbip_common.c usbip_common.h usbip_host_common.h \
+ usbip_host_common.c vhci_driver.c vhci_driver.h \
+ sysfs_utils.c sysfs_utils.h
diff --git a/tools/usb/usbip/libsrc/list.h b/tools/usb/usbip/libsrc/list.h
new file mode 100644
index 000000000..9cca24255
--- /dev/null
+++ b/tools/usb/usbip/libsrc/list.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LIST_H
+#define _LIST_H
+
+/* Stripped down implementation of linked list taken
+ * from the Linux Kernel.
+ */
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+#define POISON_POINTER_DELTA 0
+#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
+#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
+
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#endif
diff --git a/tools/usb/usbip/libsrc/names.c b/tools/usb/usbip/libsrc/names.c
new file mode 100644
index 000000000..aba7f4188
--- /dev/null
+++ b/tools/usb/usbip/libsrc/names.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * names.c -- USB name database manipulation routines
+ *
+ * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * Copyright (C) 2005 Takahiro Hirofuchi
+ * - names_deinit() is added.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "names.h"
+#include "usbip_common.h"
+
+struct vendor {
+ struct vendor *next;
+ u_int16_t vendorid;
+ char name[1];
+};
+
+struct product {
+ struct product *next;
+ u_int16_t vendorid, productid;
+ char name[1];
+};
+
+struct class {
+ struct class *next;
+ u_int8_t classid;
+ char name[1];
+};
+
+struct subclass {
+ struct subclass *next;
+ u_int8_t classid, subclassid;
+ char name[1];
+};
+
+struct protocol {
+ struct protocol *next;
+ u_int8_t classid, subclassid, protocolid;
+ char name[1];
+};
+
+struct genericstrtable {
+ struct genericstrtable *next;
+ unsigned int num;
+ char name[1];
+};
+
+
+#define HASH1 0x10
+#define HASH2 0x02
+#define HASHSZ 16
+
+static unsigned int hashnum(unsigned int num)
+{
+ unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27;
+
+ for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1)
+ if (num & mask1)
+ num ^= mask2;
+ return num & (HASHSZ-1);
+}
+
+
+static struct vendor *vendors[HASHSZ] = { NULL, };
+static struct product *products[HASHSZ] = { NULL, };
+static struct class *classes[HASHSZ] = { NULL, };
+static struct subclass *subclasses[HASHSZ] = { NULL, };
+static struct protocol *protocols[HASHSZ] = { NULL, };
+
+const char *names_vendor(u_int16_t vendorid)
+{
+ struct vendor *v;
+
+ v = vendors[hashnum(vendorid)];
+ for (; v; v = v->next)
+ if (v->vendorid == vendorid)
+ return v->name;
+ return NULL;
+}
+
+const char *names_product(u_int16_t vendorid, u_int16_t productid)
+{
+ struct product *p;
+
+ p = products[hashnum((vendorid << 16) | productid)];
+ for (; p; p = p->next)
+ if (p->vendorid == vendorid && p->productid == productid)
+ return p->name;
+ return NULL;
+}
+
+const char *names_class(u_int8_t classid)
+{
+ struct class *c;
+
+ c = classes[hashnum(classid)];
+ for (; c; c = c->next)
+ if (c->classid == classid)
+ return c->name;
+ return NULL;
+}
+
+const char *names_subclass(u_int8_t classid, u_int8_t subclassid)
+{
+ struct subclass *s;
+
+ s = subclasses[hashnum((classid << 8) | subclassid)];
+ for (; s; s = s->next)
+ if (s->classid == classid && s->subclassid == subclassid)
+ return s->name;
+ return NULL;
+}
+
+const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid)
+{
+ struct protocol *p;
+
+ p = protocols[hashnum((classid << 16) | (subclassid << 8)
+ | protocolid)];
+ for (; p; p = p->next)
+ if (p->classid == classid && p->subclassid == subclassid &&
+ p->protocolid == protocolid)
+ return p->name;
+ return NULL;
+}
+
+/* add a cleanup function by takahiro */
+struct pool {
+ struct pool *next;
+ void *mem;
+};
+
+static struct pool *pool_head;
+
+static void *my_malloc(size_t size)
+{
+ struct pool *p;
+
+ p = calloc(1, sizeof(struct pool));
+ if (!p)
+ return NULL;
+
+ p->mem = calloc(1, size);
+ if (!p->mem) {
+ free(p);
+ return NULL;
+ }
+
+ p->next = pool_head;
+ pool_head = p;
+
+ return p->mem;
+}
+
+void names_free(void)
+{
+ struct pool *pool;
+
+ if (!pool_head)
+ return;
+
+ for (pool = pool_head; pool != NULL; ) {
+ struct pool *tmp;
+
+ if (pool->mem)
+ free(pool->mem);
+
+ tmp = pool;
+ pool = pool->next;
+ free(tmp);
+ }
+}
+
+static int new_vendor(const char *name, u_int16_t vendorid)
+{
+ struct vendor *v;
+ unsigned int h = hashnum(vendorid);
+
+ v = vendors[h];
+ for (; v; v = v->next)
+ if (v->vendorid == vendorid)
+ return -1;
+ v = my_malloc(sizeof(struct vendor) + strlen(name));
+ if (!v)
+ return -1;
+ strcpy(v->name, name);
+ v->vendorid = vendorid;
+ v->next = vendors[h];
+ vendors[h] = v;
+ return 0;
+}
+
+static int new_product(const char *name, u_int16_t vendorid,
+ u_int16_t productid)
+{
+ struct product *p;
+ unsigned int h = hashnum((vendorid << 16) | productid);
+
+ p = products[h];
+ for (; p; p = p->next)
+ if (p->vendorid == vendorid && p->productid == productid)
+ return -1;
+ p = my_malloc(sizeof(struct product) + strlen(name));
+ if (!p)
+ return -1;
+ strcpy(p->name, name);
+ p->vendorid = vendorid;
+ p->productid = productid;
+ p->next = products[h];
+ products[h] = p;
+ return 0;
+}
+
+static int new_class(const char *name, u_int8_t classid)
+{
+ struct class *c;
+ unsigned int h = hashnum(classid);
+
+ c = classes[h];
+ for (; c; c = c->next)
+ if (c->classid == classid)
+ return -1;
+ c = my_malloc(sizeof(struct class) + strlen(name));
+ if (!c)
+ return -1;
+ strcpy(c->name, name);
+ c->classid = classid;
+ c->next = classes[h];
+ classes[h] = c;
+ return 0;
+}
+
+static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid)
+{
+ struct subclass *s;
+ unsigned int h = hashnum((classid << 8) | subclassid);
+
+ s = subclasses[h];
+ for (; s; s = s->next)
+ if (s->classid == classid && s->subclassid == subclassid)
+ return -1;
+ s = my_malloc(sizeof(struct subclass) + strlen(name));
+ if (!s)
+ return -1;
+ strcpy(s->name, name);
+ s->classid = classid;
+ s->subclassid = subclassid;
+ s->next = subclasses[h];
+ subclasses[h] = s;
+ return 0;
+}
+
+static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid)
+{
+ struct protocol *p;
+ unsigned int h = hashnum((classid << 16) | (subclassid << 8)
+ | protocolid);
+
+ p = protocols[h];
+ for (; p; p = p->next)
+ if (p->classid == classid && p->subclassid == subclassid
+ && p->protocolid == protocolid)
+ return -1;
+ p = my_malloc(sizeof(struct protocol) + strlen(name));
+ if (!p)
+ return -1;
+ strcpy(p->name, name);
+ p->classid = classid;
+ p->subclassid = subclassid;
+ p->protocolid = protocolid;
+ p->next = protocols[h];
+ protocols[h] = p;
+ return 0;
+}
+
+static void parse(FILE *f)
+{
+ char buf[512], *cp;
+ unsigned int linectr = 0;
+ int lastvendor = -1;
+ int lastclass = -1;
+ int lastsubclass = -1;
+ int lasthut = -1;
+ int lastlang = -1;
+ unsigned int u;
+
+ while (fgets(buf, sizeof(buf), f)) {
+ linectr++;
+ /* remove line ends */
+ cp = strchr(buf, '\r');
+ if (cp)
+ *cp = 0;
+ cp = strchr(buf, '\n');
+ if (cp)
+ *cp = 0;
+ if (buf[0] == '#' || !buf[0])
+ continue;
+ cp = buf;
+ if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' &&
+ buf[3] == 'S' && buf[4] == 'D' &&
+ buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/
+ buf[7] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'P' && buf[1] == 'H' &&
+ buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' &&
+ buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') {
+ lasthut = lastclass = lastvendor = lastsubclass = -1;
+ /*
+ * set 1 as pseudo-id to indicate that the parser is
+ * in a `L' section.
+ */
+ lastlang = 1;
+ continue;
+ }
+ if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') {
+ /* class spec */
+ cp = buf+2;
+ while (isspace(*cp))
+ cp++;
+ if (!isxdigit(*cp)) {
+ err("Invalid class spec at line %u", linectr);
+ continue;
+ }
+ u = strtoul(cp, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid class spec at line %u", linectr);
+ continue;
+ }
+ if (new_class(cp, u))
+ err("Duplicate class spec at line %u class %04x %s",
+ linectr, u, cp);
+ dbg("line %5u class %02x %s", linectr, u, cp);
+ lasthut = lastlang = lastvendor = lastsubclass = -1;
+ lastclass = u;
+ continue;
+ }
+ if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) {
+ /* audio terminal type spec */
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C'
+ && isspace(buf[3])) {
+ /* HID Descriptor bCountryCode */
+ continue;
+ }
+ if (isxdigit(*cp)) {
+ /* vendor */
+ u = strtoul(cp, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid vendor spec at line %u", linectr);
+ continue;
+ }
+ if (new_vendor(cp, u))
+ err("Duplicate vendor spec at line %u vendor %04x %s",
+ linectr, u, cp);
+ dbg("line %5u vendor %04x %s", linectr, u, cp);
+ lastvendor = u;
+ lasthut = lastlang = lastclass = lastsubclass = -1;
+ continue;
+ }
+ if (buf[0] == '\t' && isxdigit(buf[1])) {
+ /* product or subclass spec */
+ u = strtoul(buf+1, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid product/subclass spec at line %u",
+ linectr);
+ continue;
+ }
+ if (lastvendor != -1) {
+ if (new_product(cp, lastvendor, u))
+ err("Duplicate product spec at line %u product %04x:%04x %s",
+ linectr, lastvendor, u, cp);
+ dbg("line %5u product %04x:%04x %s", linectr,
+ lastvendor, u, cp);
+ continue;
+ }
+ if (lastclass != -1) {
+ if (new_subclass(cp, lastclass, u))
+ err("Duplicate subclass spec at line %u class %02x:%02x %s",
+ linectr, lastclass, u, cp);
+ dbg("line %5u subclass %02x:%02x %s", linectr,
+ lastclass, u, cp);
+ lastsubclass = u;
+ continue;
+ }
+ if (lasthut != -1) {
+ /* do not store hut */
+ continue;
+ }
+ if (lastlang != -1) {
+ /* do not store langid */
+ continue;
+ }
+ err("Product/Subclass spec without prior Vendor/Class spec at line %u",
+ linectr);
+ continue;
+ }
+ if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) {
+ /* protocol spec */
+ u = strtoul(buf+2, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid protocol spec at line %u",
+ linectr);
+ continue;
+ }
+ if (lastclass != -1 && lastsubclass != -1) {
+ if (new_protocol(cp, lastclass, lastsubclass,
+ u))
+ err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s",
+ linectr, lastclass, lastsubclass,
+ u, cp);
+ dbg("line %5u protocol %02x:%02x:%02x %s",
+ linectr, lastclass, lastsubclass, u, cp);
+ continue;
+ }
+ err("Protocol spec without prior Class and Subclass spec at line %u",
+ linectr);
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'I' &&
+ buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'U' &&
+ buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ lastlang = lastclass = lastvendor = lastsubclass = -1;
+ /*
+ * set 1 as pseudo-id to indicate that the parser is
+ * in a `HUT' section.
+ */
+ lasthut = 1;
+ continue;
+ }
+ if (buf[0] == 'R' && buf[1] == ' ')
+ continue;
+
+ if (buf[0] == 'V' && buf[1] == 'T')
+ continue;
+
+ err("Unknown line at line %u", linectr);
+ }
+}
+
+
+int names_init(char *n)
+{
+ FILE *f;
+
+ f = fopen(n, "r");
+ if (!f)
+ return errno;
+
+ parse(f);
+ fclose(f);
+ return 0;
+}
diff --git a/tools/usb/usbip/libsrc/names.h b/tools/usb/usbip/libsrc/names.h
new file mode 100644
index 000000000..b39958230
--- /dev/null
+++ b/tools/usb/usbip/libsrc/names.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * names.h -- USB name database manipulation routines
+ *
+ * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * Copyright (C) 2005 Takahiro Hirofuchi
+ * - names_free() is added.
+ */
+
+#ifndef _NAMES_H
+#define _NAMES_H
+
+#include <sys/types.h>
+
+/* used by usbip_common.c */
+extern const char *names_vendor(u_int16_t vendorid);
+extern const char *names_product(u_int16_t vendorid, u_int16_t productid);
+extern const char *names_class(u_int8_t classid);
+extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid);
+extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid);
+extern int names_init(char *n);
+extern void names_free(void);
+
+#endif /* _NAMES_H */
diff --git a/tools/usb/usbip/libsrc/sysfs_utils.c b/tools/usb/usbip/libsrc/sysfs_utils.c
new file mode 100644
index 000000000..14d5e67d3
--- /dev/null
+++ b/tools/usb/usbip/libsrc/sysfs_utils.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "sysfs_utils.h"
+#include "usbip_common.h"
+
+int write_sysfs_attribute(const char *attr_path, const char *new_value,
+ size_t len)
+{
+ int fd;
+ int length;
+
+ fd = open(attr_path, O_WRONLY);
+ if (fd < 0) {
+ dbg("error opening attribute %s", attr_path);
+ return -1;
+ }
+
+ length = write(fd, new_value, len);
+ if (length < 0) {
+ dbg("error writing to attribute %s", attr_path);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/tools/usb/usbip/libsrc/sysfs_utils.h b/tools/usb/usbip/libsrc/sysfs_utils.h
new file mode 100644
index 000000000..0cd5f17e7
--- /dev/null
+++ b/tools/usb/usbip/libsrc/sysfs_utils.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __SYSFS_UTILS_H
+#define __SYSFS_UTILS_H
+
+int write_sysfs_attribute(const char *attr_path, const char *new_value,
+ size_t len);
+
+#endif
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c
new file mode 100644
index 000000000..f4734f552
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_common.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <libudev.h>
+#include "usbip_common.h"
+#include "names.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+int usbip_use_syslog;
+int usbip_use_stderr;
+int usbip_use_debug;
+
+extern struct udev *udev_context;
+
+struct speed_string {
+ int num;
+ char *speed;
+ char *desc;
+};
+
+static const struct speed_string speed_strings[] = {
+ { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"},
+ { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" },
+ { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" },
+ { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
+ { USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
+ { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
+ { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" },
+ { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" },
+ { 0, NULL, NULL }
+};
+
+struct portst_string {
+ int num;
+ char *desc;
+};
+
+static struct portst_string portst_strings[] = {
+ { SDEV_ST_AVAILABLE, "Device Available" },
+ { SDEV_ST_USED, "Device in Use" },
+ { SDEV_ST_ERROR, "Device Error"},
+ { VDEV_ST_NULL, "Port Available"},
+ { VDEV_ST_NOTASSIGNED, "Port Initializing"},
+ { VDEV_ST_USED, "Port in Use"},
+ { VDEV_ST_ERROR, "Port Error"},
+ { 0, NULL}
+};
+
+const char *usbip_status_string(int32_t status)
+{
+ for (int i = 0; portst_strings[i].desc != NULL; i++)
+ if (portst_strings[i].num == status)
+ return portst_strings[i].desc;
+
+ return "Unknown Status";
+}
+
+const char *usbip_speed_string(int num)
+{
+ for (int i = 0; speed_strings[i].speed != NULL; i++)
+ if (speed_strings[i].num == num)
+ return speed_strings[i].desc;
+
+ return "Unknown Speed";
+}
+
+struct op_common_status_string {
+ int num;
+ char *desc;
+};
+
+static struct op_common_status_string op_common_status_strings[] = {
+ { ST_OK, "Request Completed Successfully" },
+ { ST_NA, "Request Failed" },
+ { ST_DEV_BUSY, "Device busy (exported)" },
+ { ST_DEV_ERR, "Device in error state" },
+ { ST_NODEV, "Device not found" },
+ { ST_ERROR, "Unexpected response" },
+ { 0, NULL}
+};
+
+const char *usbip_op_common_status_string(int status)
+{
+ for (int i = 0; op_common_status_strings[i].desc != NULL; i++)
+ if (op_common_status_strings[i].num == status)
+ return op_common_status_strings[i].desc;
+
+ return "Unknown Op Common Status";
+}
+
+#define DBG_UDEV_INTEGER(name)\
+ dbg("%-20s = %x", to_string(name), (int) udev->name)
+
+#define DBG_UINF_INTEGER(name)\
+ dbg("%-20s = %x", to_string(name), (int) uinf->name)
+
+void dump_usb_interface(struct usbip_usb_interface *uinf)
+{
+ char buff[100];
+
+ usbip_names_get_class(buff, sizeof(buff),
+ uinf->bInterfaceClass,
+ uinf->bInterfaceSubClass,
+ uinf->bInterfaceProtocol);
+ dbg("%-20s = %s", "Interface(C/SC/P)", buff);
+}
+
+void dump_usb_device(struct usbip_usb_device *udev)
+{
+ char buff[100];
+
+ dbg("%-20s = %s", "path", udev->path);
+ dbg("%-20s = %s", "busid", udev->busid);
+
+ usbip_names_get_class(buff, sizeof(buff),
+ udev->bDeviceClass,
+ udev->bDeviceSubClass,
+ udev->bDeviceProtocol);
+ dbg("%-20s = %s", "Device(C/SC/P)", buff);
+
+ DBG_UDEV_INTEGER(bcdDevice);
+
+ usbip_names_get_product(buff, sizeof(buff),
+ udev->idVendor,
+ udev->idProduct);
+ dbg("%-20s = %s", "Vendor/Product", buff);
+
+ DBG_UDEV_INTEGER(bNumConfigurations);
+ DBG_UDEV_INTEGER(bNumInterfaces);
+
+ dbg("%-20s = %s", "speed",
+ usbip_speed_string(udev->speed));
+
+ DBG_UDEV_INTEGER(busnum);
+ DBG_UDEV_INTEGER(devnum);
+}
+
+
+int read_attr_value(struct udev_device *dev, const char *name,
+ const char *format)
+{
+ const char *attr;
+ int num = 0;
+ int ret;
+
+ attr = udev_device_get_sysattr_value(dev, name);
+ if (!attr) {
+ err("udev_device_get_sysattr_value failed");
+ goto err;
+ }
+
+ /* The client chooses the device configuration
+ * when attaching it so right after being bound
+ * to usbip-host on the server the device will
+ * have no configuration.
+ * Therefore, attributes such as bConfigurationValue
+ * and bNumInterfaces will not exist and sscanf will
+ * fail. Check for these cases and don't treat them
+ * as errors.
+ */
+
+ ret = sscanf(attr, format, &num);
+ if (ret < 1) {
+ if (strcmp(name, "bConfigurationValue") &&
+ strcmp(name, "bNumInterfaces")) {
+ err("sscanf failed for attribute %s", name);
+ goto err;
+ }
+ }
+
+err:
+
+ return num;
+}
+
+
+int read_attr_speed(struct udev_device *dev)
+{
+ const char *speed;
+
+ speed = udev_device_get_sysattr_value(dev, "speed");
+ if (!speed) {
+ err("udev_device_get_sysattr_value failed");
+ goto err;
+ }
+
+ for (int i = 0; speed_strings[i].speed != NULL; i++) {
+ if (!strcmp(speed, speed_strings[i].speed))
+ return speed_strings[i].num;
+ }
+
+err:
+
+ return USB_SPEED_UNKNOWN;
+}
+
+#define READ_ATTR(object, type, dev, name, format) \
+ do { \
+ (object)->name = (type) read_attr_value(dev, to_string(name), \
+ format); \
+ } while (0)
+
+
+int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
+{
+ uint32_t busnum, devnum;
+ const char *path, *name;
+
+ READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n");
+
+ READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n");
+ READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n");
+ READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n");
+
+ READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n");
+
+ READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n");
+ udev->speed = read_attr_speed(sdev);
+
+ path = udev_device_get_syspath(sdev);
+ name = udev_device_get_sysname(sdev);
+
+ strncpy(udev->path, path, SYSFS_PATH_MAX - 1);
+ udev->path[SYSFS_PATH_MAX - 1] = '\0';
+ strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1);
+ udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';
+
+ sscanf(name, "%u-%u", &busnum, &devnum);
+ udev->busnum = busnum;
+
+ return 0;
+}
+
+int read_usb_interface(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf)
+{
+ char busid[SYSFS_BUS_ID_SIZE];
+ int size;
+ struct udev_device *sif;
+
+ size = snprintf(busid, sizeof(busid), "%s:%d.%d",
+ udev->busid, udev->bConfigurationValue, i);
+ if (size < 0 || (unsigned int)size >= sizeof(busid)) {
+ err("busid length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(busid));
+ return -1;
+ }
+
+ sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid);
+ if (!sif) {
+ err("udev_device_new_from_subsystem_sysname %s failed", busid);
+ return -1;
+ }
+
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n");
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n");
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n");
+
+ return 0;
+}
+
+int usbip_names_init(char *f)
+{
+ return names_init(f);
+}
+
+void usbip_names_free(void)
+{
+ names_free();
+}
+
+void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
+ uint16_t product)
+{
+ const char *prod, *vend;
+
+ prod = names_product(vendor, product);
+ if (!prod)
+ prod = "unknown product";
+
+
+ vend = names_vendor(vendor);
+ if (!vend)
+ vend = "unknown vendor";
+
+ snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
+}
+
+void usbip_names_get_class(char *buff, size_t size, uint8_t class,
+ uint8_t subclass, uint8_t protocol)
+{
+ const char *c, *s, *p;
+
+ if (class == 0 && subclass == 0 && protocol == 0) {
+ snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol);
+ return;
+ }
+
+ p = names_protocol(class, subclass, protocol);
+ if (!p)
+ p = "unknown protocol";
+
+ s = names_subclass(class, subclass);
+ if (!s)
+ s = "unknown subclass";
+
+ c = names_class(class);
+ if (!c)
+ c = "unknown class";
+
+ snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
+}
diff --git a/tools/usb/usbip/libsrc/usbip_common.h b/tools/usb/usbip/libsrc/usbip_common.h
new file mode 100644
index 000000000..73a367a7f
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_common.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_COMMON_H
+#define __USBIP_COMMON_H
+
+#include <libudev.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <syslog.h>
+#include <unistd.h>
+#include <linux/usb/ch9.h>
+#include <linux/usbip.h>
+
+#ifndef USBIDS_FILE
+#define USBIDS_FILE "/usr/share/hwdata/usb.ids"
+#endif
+
+#ifndef VHCI_STATE_PATH
+#define VHCI_STATE_PATH "/var/run/vhci_hcd"
+#endif
+
+#define VUDC_DEVICE_DESCR_FILE "dev_desc"
+
+/* kernel module names */
+#define USBIP_CORE_MOD_NAME "usbip-core"
+#define USBIP_HOST_DRV_NAME "usbip-host"
+#define USBIP_DEVICE_DRV_NAME "usbip-vudc"
+#define USBIP_VHCI_DRV_NAME "vhci_hcd"
+
+/* sysfs constants */
+#define SYSFS_MNT_PATH "/sys"
+#define SYSFS_BUS_NAME "bus"
+#define SYSFS_BUS_TYPE "usb"
+#define SYSFS_DRIVERS_NAME "drivers"
+
+#define SYSFS_PATH_MAX 256
+#define SYSFS_BUS_ID_SIZE 32
+
+/* Defines for op_code status in server/client op_common PDUs */
+#define ST_OK 0x00
+#define ST_NA 0x01
+ /* Device requested for import is not available */
+#define ST_DEV_BUSY 0x02
+ /* Device requested for import is in error state */
+#define ST_DEV_ERR 0x03
+#define ST_NODEV 0x04
+#define ST_ERROR 0x05
+
+extern int usbip_use_syslog;
+extern int usbip_use_stderr;
+extern int usbip_use_debug ;
+
+#define PROGNAME "usbip"
+
+#define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME
+#define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \
+ __FILE__, __LINE__, __func__
+
+#define err(fmt, args...) \
+ do { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, pr_fmt(fmt), "error", ##args); \
+ } \
+ } while (0)
+
+#define info(fmt, args...) \
+ do { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, pr_fmt(fmt), "info", ##args); \
+ } \
+ } while (0)
+
+#define dbg(fmt, args...) \
+ do { \
+ if (usbip_use_debug) { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, dbg_fmt(fmt), ##args); \
+ } \
+ } \
+ } while (0)
+
+#define BUG() \
+ do { \
+ err("sorry, it's a bug!"); \
+ abort(); \
+ } while (0)
+
+struct usbip_usb_interface {
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t padding; /* alignment */
+} __attribute__((packed));
+
+struct usbip_usb_device {
+ char path[SYSFS_PATH_MAX];
+ char busid[SYSFS_BUS_ID_SIZE];
+
+ uint32_t busnum;
+ uint32_t devnum;
+ uint32_t speed;
+
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bConfigurationValue;
+ uint8_t bNumConfigurations;
+ uint8_t bNumInterfaces;
+} __attribute__((packed));
+
+#define to_string(s) #s
+
+void dump_usb_interface(struct usbip_usb_interface *);
+void dump_usb_device(struct usbip_usb_device *);
+int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev);
+int read_attr_value(struct udev_device *dev, const char *name,
+ const char *format);
+int read_usb_interface(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf);
+
+const char *usbip_speed_string(int num);
+const char *usbip_status_string(int32_t status);
+const char *usbip_op_common_status_string(int status);
+
+int usbip_names_init(char *);
+void usbip_names_free(void);
+void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
+ uint16_t product);
+void usbip_names_get_class(char *buff, size_t size, uint8_t class,
+ uint8_t subclass, uint8_t protocol);
+
+#endif /* __USBIP_COMMON_H */
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
new file mode 100644
index 000000000..c9b3619d8
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
+ * 2015 Samsung Electronics
+ * Author: Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ *
+ * Based on tools/usb/usbip/libsrc/usbip_host_driver.c, which is:
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <linux/usb/ch9.h>
+
+#include <unistd.h>
+
+#include "usbip_host_common.h"
+#include "usbip_device_driver.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+#define copy_descr_attr16(dev, descr, attr) \
+ ((dev)->attr = le16toh((descr)->attr)) \
+
+#define copy_descr_attr(dev, descr, attr) \
+ ((dev)->attr = (descr)->attr) \
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static struct {
+ enum usb_device_speed speed;
+ const char *name;
+} speed_names[] = {
+ {
+ .speed = USB_SPEED_UNKNOWN,
+ .name = "UNKNOWN",
+ },
+ {
+ .speed = USB_SPEED_LOW,
+ .name = "low-speed",
+ },
+ {
+ .speed = USB_SPEED_FULL,
+ .name = "full-speed",
+ },
+ {
+ .speed = USB_SPEED_HIGH,
+ .name = "high-speed",
+ },
+ {
+ .speed = USB_SPEED_WIRELESS,
+ .name = "wireless",
+ },
+ {
+ .speed = USB_SPEED_SUPER,
+ .name = "super-speed",
+ },
+ {
+ .speed = USB_SPEED_SUPER_PLUS,
+ .name = "super-speed-plus",
+ },
+};
+
+static
+int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
+{
+ const char *path, *name;
+ char filepath[SYSFS_PATH_MAX];
+ struct usb_device_descriptor descr;
+ unsigned int i;
+ FILE *fd = NULL;
+ struct udev_device *plat;
+ const char *speed;
+ size_t ret;
+
+ plat = udev_device_get_parent(sdev);
+ path = udev_device_get_syspath(plat);
+ snprintf(filepath, SYSFS_PATH_MAX, "%s/%s",
+ path, VUDC_DEVICE_DESCR_FILE);
+ fd = fopen(filepath, "r");
+ if (!fd)
+ return -1;
+ ret = fread((char *) &descr, sizeof(descr), 1, fd);
+ if (ret != 1) {
+ err("Cannot read vudc device descr file: %s", strerror(errno));
+ goto err;
+ }
+ fclose(fd);
+
+ copy_descr_attr(dev, &descr, bDeviceClass);
+ copy_descr_attr(dev, &descr, bDeviceSubClass);
+ copy_descr_attr(dev, &descr, bDeviceProtocol);
+ copy_descr_attr(dev, &descr, bNumConfigurations);
+ copy_descr_attr16(dev, &descr, idVendor);
+ copy_descr_attr16(dev, &descr, idProduct);
+ copy_descr_attr16(dev, &descr, bcdDevice);
+
+ strncpy(dev->path, path, SYSFS_PATH_MAX - 1);
+ dev->path[SYSFS_PATH_MAX - 1] = '\0';
+
+ dev->speed = USB_SPEED_UNKNOWN;
+ speed = udev_device_get_sysattr_value(sdev, "current_speed");
+ if (speed) {
+ for (i = 0; i < ARRAY_SIZE(speed_names); i++) {
+ if (!strcmp(speed_names[i].name, speed)) {
+ dev->speed = speed_names[i].speed;
+ break;
+ }
+ }
+ }
+
+ /* Only used for user output, little sense to output them in general */
+ dev->bNumInterfaces = 0;
+ dev->bConfigurationValue = 0;
+ dev->busnum = 0;
+
+ name = udev_device_get_sysname(plat);
+ strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE - 1);
+ dev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';
+ return 0;
+err:
+ fclose(fd);
+ return -1;
+}
+
+static int is_my_device(struct udev_device *dev)
+{
+ const char *driver;
+
+ driver = udev_device_get_property_value(dev, "USB_UDC_NAME");
+ return driver != NULL && !strcmp(driver, USBIP_DEVICE_DRV_NAME);
+}
+
+static int usbip_device_driver_open(struct usbip_host_driver *hdriver)
+{
+ int ret;
+
+ hdriver->ndevs = 0;
+ INIT_LIST_HEAD(&hdriver->edev_list);
+
+ ret = usbip_generic_driver_open(hdriver);
+ if (ret || hdriver->ndevs == 0)
+ info("please load " USBIP_CORE_MOD_NAME ".ko and "
+ USBIP_DEVICE_DRV_NAME ".ko");
+
+ return ret;
+}
+
+struct usbip_host_driver device_driver = {
+ .edev_list = LIST_HEAD_INIT(device_driver.edev_list),
+ .udev_subsystem = "udc",
+ .ops = {
+ .open = usbip_device_driver_open,
+ .close = usbip_generic_driver_close,
+ .refresh_device_list = usbip_generic_refresh_device_list,
+ .get_device = usbip_generic_get_device,
+ .read_device = read_usb_vudc_device,
+ .is_my_device = is_my_device,
+ },
+};
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.h b/tools/usb/usbip/libsrc/usbip_device_driver.h
new file mode 100644
index 000000000..1ce0bbd75
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
+ * 2015 Samsung Electronics
+ * Author: Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ *
+ * Based on tools/usb/usbip/libsrc/usbip_host_driver.c, which is:
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_DEVICE_DRIVER_H
+#define __USBIP_DEVICE_DRIVER_H
+
+#include <stdint.h>
+#include "usbip_common.h"
+#include "usbip_host_common.h"
+#include "list.h"
+
+extern struct usbip_host_driver device_driver;
+
+#endif /* __USBIP_DEVICE_DRIVER_H */
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c
new file mode 100644
index 000000000..01599cb2f
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_host_common.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ *
+ * Refactored from usbip_host_driver.c, which is:
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <libudev.h>
+
+#include "usbip_common.h"
+#include "usbip_host_common.h"
+#include "list.h"
+#include "sysfs_utils.h"
+
+extern struct udev *udev_context;
+
+static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
+{
+ char status_attr_path[SYSFS_PATH_MAX];
+ int size;
+ int fd;
+ int length;
+ char status[2] = { 0 };
+ int value = 0;
+
+ size = snprintf(status_attr_path, sizeof(status_attr_path),
+ "%s/usbip_status", udev->path);
+ if (size < 0 || (unsigned int)size >= sizeof(status_attr_path)) {
+ err("usbip_status path length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(status_attr_path));
+ return -1;
+ }
+
+
+ fd = open(status_attr_path, O_RDONLY);
+ if (fd < 0) {
+ err("error opening attribute %s", status_attr_path);
+ return -1;
+ }
+
+ length = read(fd, status, 1);
+ if (length < 0) {
+ err("error reading attribute %s", status_attr_path);
+ close(fd);
+ return -1;
+ }
+
+ value = atoi(status);
+ close(fd);
+ return value;
+}
+
+static
+struct usbip_exported_device *usbip_exported_device_new(
+ struct usbip_host_driver *hdriver, const char *sdevpath)
+{
+ struct usbip_exported_device *edev = NULL;
+ struct usbip_exported_device *edev_old;
+ size_t size;
+ int i;
+
+ edev = calloc(1, sizeof(struct usbip_exported_device));
+
+ edev->sudev =
+ udev_device_new_from_syspath(udev_context, sdevpath);
+ if (!edev->sudev) {
+ err("udev_device_new_from_syspath: %s", sdevpath);
+ goto err;
+ }
+
+ if (hdriver->ops.read_device(edev->sudev, &edev->udev) < 0)
+ goto err;
+
+ edev->status = read_attr_usbip_status(&edev->udev);
+ if (edev->status < 0)
+ goto err;
+
+ /* reallocate buffer to include usb interface data */
+ size = sizeof(struct usbip_exported_device) +
+ edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface);
+
+ edev_old = edev;
+ edev = realloc(edev, size);
+ if (!edev) {
+ edev = edev_old;
+ dbg("realloc failed");
+ goto err;
+ }
+
+ for (i = 0; i < edev->udev.bNumInterfaces; i++) {
+ /* vudc does not support reading interfaces */
+ if (!hdriver->ops.read_interface)
+ break;
+ hdriver->ops.read_interface(&edev->udev, i, &edev->uinf[i]);
+ }
+
+ return edev;
+err:
+ if (edev->sudev)
+ udev_device_unref(edev->sudev);
+ if (edev)
+ free(edev);
+
+ return NULL;
+}
+
+static int refresh_exported_devices(struct usbip_host_driver *hdriver)
+{
+ struct usbip_exported_device *edev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+
+ enumerate = udev_enumerate_new(udev_context);
+ udev_enumerate_add_match_subsystem(enumerate, hdriver->udev_subsystem);
+ udev_enumerate_scan_devices(enumerate);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev_context,
+ path);
+ if (dev == NULL)
+ continue;
+
+ /* Check whether device uses usbip driver. */
+ if (hdriver->ops.is_my_device(dev)) {
+ edev = usbip_exported_device_new(hdriver, path);
+ if (!edev) {
+ dbg("usbip_exported_device_new failed");
+ continue;
+ }
+
+ list_add(&edev->node, &hdriver->edev_list);
+ hdriver->ndevs++;
+ }
+ }
+
+ if (hdriver->ndevs == 0)
+ info("Please load appropriate modules or export devices.");
+
+ return 0;
+}
+
+static void usbip_exported_device_destroy(struct list_head *devs)
+{
+ struct list_head *i, *tmp;
+ struct usbip_exported_device *edev;
+
+ list_for_each_safe(i, tmp, devs) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ list_del(i);
+ free(edev);
+ }
+}
+
+int usbip_generic_driver_open(struct usbip_host_driver *hdriver)
+{
+ int rc;
+
+ udev_context = udev_new();
+ if (!udev_context) {
+ err("udev_new failed");
+ return -1;
+ }
+
+ rc = refresh_exported_devices(hdriver);
+ if (rc < 0)
+ goto err;
+ return 0;
+err:
+ udev_unref(udev_context);
+ return -1;
+}
+
+void usbip_generic_driver_close(struct usbip_host_driver *hdriver)
+{
+ if (!hdriver)
+ return;
+
+ usbip_exported_device_destroy(&hdriver->edev_list);
+
+ udev_unref(udev_context);
+}
+
+int usbip_generic_refresh_device_list(struct usbip_host_driver *hdriver)
+{
+ int rc;
+
+ usbip_exported_device_destroy(&hdriver->edev_list);
+
+ hdriver->ndevs = 0;
+ INIT_LIST_HEAD(&hdriver->edev_list);
+
+ rc = refresh_exported_devices(hdriver);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int usbip_export_device(struct usbip_exported_device *edev, int sockfd)
+{
+ char attr_name[] = "usbip_sockfd";
+ char sockfd_attr_path[SYSFS_PATH_MAX];
+ int size;
+ char sockfd_buff[30];
+ int ret;
+
+ if (edev->status != SDEV_ST_AVAILABLE) {
+ dbg("device not available: %s", edev->udev.busid);
+ switch (edev->status) {
+ case SDEV_ST_ERROR:
+ dbg("status SDEV_ST_ERROR");
+ ret = ST_DEV_ERR;
+ break;
+ case SDEV_ST_USED:
+ dbg("status SDEV_ST_USED");
+ ret = ST_DEV_BUSY;
+ break;
+ default:
+ dbg("status unknown: 0x%x", edev->status);
+ ret = -1;
+ }
+ return ret;
+ }
+
+ /* only the first interface is true */
+ size = snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s",
+ edev->udev.path, attr_name);
+ if (size < 0 || (unsigned int)size >= sizeof(sockfd_attr_path)) {
+ err("exported device path length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(sockfd_attr_path));
+ return -1;
+ }
+
+ size = snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+ if (size < 0 || (unsigned int)size >= sizeof(sockfd_buff)) {
+ err("socket length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(sockfd_buff));
+ return -1;
+ }
+
+ ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff,
+ strlen(sockfd_buff));
+ if (ret < 0) {
+ err("write_sysfs_attribute failed: sockfd %s to %s",
+ sockfd_buff, sockfd_attr_path);
+ return ret;
+ }
+
+ info("connect: %s", edev->udev.busid);
+
+ return ret;
+}
+
+struct usbip_exported_device *usbip_generic_get_device(
+ struct usbip_host_driver *hdriver, int num)
+{
+ struct list_head *i;
+ struct usbip_exported_device *edev;
+ int cnt = 0;
+
+ list_for_each(i, &hdriver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ if (num == cnt)
+ return edev;
+ cnt++;
+ }
+
+ return NULL;
+}
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.h b/tools/usb/usbip/libsrc/usbip_host_common.h
new file mode 100644
index 000000000..f46967c0a
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_host_common.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ *
+ * Refactored from usbip_host_driver.c, which is:
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_HOST_COMMON_H
+#define __USBIP_HOST_COMMON_H
+
+#include <stdint.h>
+#include <libudev.h>
+#include <errno.h>
+#include "list.h"
+#include "usbip_common.h"
+#include "sysfs_utils.h"
+
+struct usbip_host_driver;
+
+struct usbip_host_driver_ops {
+ int (*open)(struct usbip_host_driver *hdriver);
+ void (*close)(struct usbip_host_driver *hdriver);
+ int (*refresh_device_list)(struct usbip_host_driver *hdriver);
+ struct usbip_exported_device * (*get_device)(
+ struct usbip_host_driver *hdriver, int num);
+
+ int (*read_device)(struct udev_device *sdev,
+ struct usbip_usb_device *dev);
+ int (*read_interface)(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf);
+ int (*is_my_device)(struct udev_device *udev);
+};
+
+struct usbip_host_driver {
+ int ndevs;
+ /* list of exported device */
+ struct list_head edev_list;
+ const char *udev_subsystem;
+ struct usbip_host_driver_ops ops;
+};
+
+struct usbip_exported_device {
+ struct udev_device *sudev;
+ int32_t status;
+ struct usbip_usb_device udev;
+ struct list_head node;
+ struct usbip_usb_interface uinf[];
+};
+
+/* External API to access the driver */
+static inline int usbip_driver_open(struct usbip_host_driver *hdriver)
+{
+ if (!hdriver->ops.open)
+ return -EOPNOTSUPP;
+ return hdriver->ops.open(hdriver);
+}
+
+static inline void usbip_driver_close(struct usbip_host_driver *hdriver)
+{
+ if (!hdriver->ops.close)
+ return;
+ hdriver->ops.close(hdriver);
+}
+
+static inline int usbip_refresh_device_list(struct usbip_host_driver *hdriver)
+{
+ if (!hdriver->ops.refresh_device_list)
+ return -EOPNOTSUPP;
+ return hdriver->ops.refresh_device_list(hdriver);
+}
+
+static inline struct usbip_exported_device *
+usbip_get_device(struct usbip_host_driver *hdriver, int num)
+{
+ if (!hdriver->ops.get_device)
+ return NULL;
+ return hdriver->ops.get_device(hdriver, num);
+}
+
+/* Helper functions for implementing driver backend */
+int usbip_generic_driver_open(struct usbip_host_driver *hdriver);
+void usbip_generic_driver_close(struct usbip_host_driver *hdriver);
+int usbip_generic_refresh_device_list(struct usbip_host_driver *hdriver);
+int usbip_export_device(struct usbip_exported_device *edev, int sockfd);
+struct usbip_exported_device *usbip_generic_get_device(
+ struct usbip_host_driver *hdriver, int num);
+
+#endif /* __USBIP_HOST_COMMON_H */
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c
new file mode 100644
index 000000000..bd8a6b84d
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ */
+
+#include <unistd.h>
+#include <libudev.h>
+
+#include "usbip_host_common.h"
+#include "usbip_host_driver.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+static int is_my_device(struct udev_device *dev)
+{
+ const char *driver;
+
+ driver = udev_device_get_driver(dev);
+ return driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME);
+}
+
+static int usbip_host_driver_open(struct usbip_host_driver *hdriver)
+{
+ int ret;
+
+ hdriver->ndevs = 0;
+ INIT_LIST_HEAD(&hdriver->edev_list);
+
+ ret = usbip_generic_driver_open(hdriver);
+ if (ret || hdriver->ndevs == 0)
+ info("please load " USBIP_CORE_MOD_NAME ".ko and "
+ USBIP_HOST_DRV_NAME ".ko");
+
+ return ret;
+}
+
+struct usbip_host_driver host_driver = {
+ .edev_list = LIST_HEAD_INIT(host_driver.edev_list),
+ .udev_subsystem = "usb",
+ .ops = {
+ .open = usbip_host_driver_open,
+ .close = usbip_generic_driver_close,
+ .refresh_device_list = usbip_generic_refresh_device_list,
+ .get_device = usbip_generic_get_device,
+ .read_device = read_usb_device,
+ .read_interface = read_usb_interface,
+ .is_my_device = is_my_device,
+ },
+};
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.h b/tools/usb/usbip/libsrc/usbip_host_driver.h
new file mode 100644
index 000000000..6ba996c5a
--- /dev/null
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ */
+
+#ifndef __USBIP_HOST_DRIVER_H
+#define __USBIP_HOST_DRIVER_H
+
+#include <stdint.h>
+#include "usbip_common.h"
+#include "list.h"
+#include "usbip_host_common.h"
+
+extern struct usbip_host_driver host_driver;
+
+#endif /* __USBIP_HOST_DRIVER_H */
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
new file mode 100644
index 000000000..4ca3783ee
--- /dev/null
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#include "usbip_common.h"
+#include "vhci_driver.h"
+#include <limits.h>
+#include <netdb.h>
+#include <libudev.h>
+#include <dirent.h>
+#include "sysfs_utils.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+struct usbip_vhci_driver *vhci_driver;
+struct udev *udev_context;
+
+static struct usbip_imported_device *
+imported_device_init(struct usbip_imported_device *idev, char *busid)
+{
+ struct udev_device *sudev;
+
+ sudev = udev_device_new_from_subsystem_sysname(udev_context,
+ "usb", busid);
+ if (!sudev) {
+ dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
+ goto err;
+ }
+ read_usb_device(sudev, &idev->udev);
+ udev_device_unref(sudev);
+
+ return idev;
+
+err:
+ return NULL;
+}
+
+static int parse_status(const char *value)
+{
+ int ret = 0;
+ char *c;
+
+ /* skip a header line */
+ c = strchr(value, '\n');
+ if (!c)
+ return -1;
+ c++;
+
+ while (*c != '\0') {
+ int port, status, speed, devid;
+ int sockfd;
+ char lbusid[SYSFS_BUS_ID_SIZE];
+ struct usbip_imported_device *idev;
+ char hub[3];
+
+ ret = sscanf(c, "%2s %d %d %d %x %u %31s\n",
+ hub, &port, &status, &speed,
+ &devid, &sockfd, lbusid);
+
+ if (ret < 5) {
+ dbg("sscanf failed: %d", ret);
+ BUG();
+ }
+
+ dbg("hub %s port %d status %d speed %d devid %x",
+ hub, port, status, speed, devid);
+ dbg("sockfd %u lbusid %s", sockfd, lbusid);
+
+ /* if a device is connected, look at it */
+ idev = &vhci_driver->idev[port];
+ memset(idev, 0, sizeof(*idev));
+
+ if (strncmp("hs", hub, 2) == 0)
+ idev->hub = HUB_SPEED_HIGH;
+ else /* strncmp("ss", hub, 2) == 0 */
+ idev->hub = HUB_SPEED_SUPER;
+
+ idev->port = port;
+ idev->status = status;
+
+ idev->devid = devid;
+
+ idev->busnum = (devid >> 16);
+ idev->devnum = (devid & 0x0000ffff);
+
+ if (idev->status != VDEV_ST_NULL
+ && idev->status != VDEV_ST_NOTASSIGNED) {
+ idev = imported_device_init(idev, lbusid);
+ if (!idev) {
+ dbg("imported_device_init failed");
+ return -1;
+ }
+ }
+
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ break;
+ c++;
+ }
+
+ dbg("exit");
+
+ return 0;
+}
+
+#define MAX_STATUS_NAME 18
+
+static int refresh_imported_device_list(void)
+{
+ const char *attr_status;
+ char status[MAX_STATUS_NAME+1] = "status";
+ int i, ret;
+
+ for (i = 0; i < vhci_driver->ncontrollers; i++) {
+ if (i > 0)
+ snprintf(status, sizeof(status), "status.%d", i);
+
+ attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
+ status);
+ if (!attr_status) {
+ err("udev_device_get_sysattr_value failed");
+ return -1;
+ }
+
+ dbg("controller %d", i);
+
+ ret = parse_status(attr_status);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int get_nports(struct udev_device *hc_device)
+{
+ const char *attr_nports;
+
+ attr_nports = udev_device_get_sysattr_value(hc_device, "nports");
+ if (!attr_nports) {
+ err("udev_device_get_sysattr_value nports failed");
+ return -1;
+ }
+
+ return (int)strtoul(attr_nports, NULL, 10);
+}
+
+static int vhci_hcd_filter(const struct dirent *dirent)
+{
+ return !strncmp(dirent->d_name, "vhci_hcd.", 9);
+}
+
+static int get_ncontrollers(void)
+{
+ struct dirent **namelist;
+ struct udev_device *platform;
+ int n;
+
+ platform = udev_device_get_parent(vhci_driver->hc_device);
+ if (platform == NULL)
+ return -1;
+
+ n = scandir(udev_device_get_syspath(platform), &namelist, vhci_hcd_filter, NULL);
+ if (n < 0)
+ err("scandir failed");
+ else {
+ for (int i = 0; i < n; i++)
+ free(namelist[i]);
+ free(namelist);
+ }
+
+ return n;
+}
+
+/*
+ * Read the given port's record.
+ *
+ * To avoid buffer overflow we will read the entire line and
+ * validate each part's size. The initial buffer is padded by 4 to
+ * accommodate the 2 spaces, 1 newline and an additional character
+ * which is needed to properly validate the 3rd part without it being
+ * truncated to an acceptable length.
+ */
+static int read_record(int rhport, char *host, unsigned long host_len,
+ char *port, unsigned long port_len, char *busid)
+{
+ int part;
+ FILE *file;
+ char path[PATH_MAX+1];
+ char *buffer, *start, *end;
+ char delim[] = {' ', ' ', '\n'};
+ int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
+ size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
+
+ buffer = malloc(buffer_len);
+ if (!buffer)
+ return -1;
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ file = fopen(path, "r");
+ if (!file) {
+ err("fopen");
+ free(buffer);
+ return -1;
+ }
+
+ if (fgets(buffer, buffer_len, file) == NULL) {
+ err("fgets");
+ free(buffer);
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ /* validate the length of each of the 3 parts */
+ start = buffer;
+ for (part = 0; part < 3; part++) {
+ end = strchr(start, delim[part]);
+ if (end == NULL || (end - start) > max_len[part]) {
+ free(buffer);
+ return -1;
+ }
+ start = end + 1;
+ }
+
+ if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
+ err("sscanf");
+ free(buffer);
+ return -1;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int usbip_vhci_driver_open(void)
+{
+ int nports;
+ struct udev_device *hc_device;
+
+ udev_context = udev_new();
+ if (!udev_context) {
+ err("udev_new failed");
+ return -1;
+ }
+
+ /* will be freed in usbip_driver_close() */
+ hc_device =
+ udev_device_new_from_subsystem_sysname(udev_context,
+ USBIP_VHCI_BUS_TYPE,
+ USBIP_VHCI_DEVICE_NAME);
+ if (!hc_device) {
+ err("udev_device_new_from_subsystem_sysname failed");
+ goto err;
+ }
+
+ nports = get_nports(hc_device);
+ if (nports <= 0) {
+ err("no available ports");
+ goto err;
+ }
+ dbg("available ports: %d", nports);
+
+ vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver) +
+ nports * sizeof(struct usbip_imported_device));
+ if (!vhci_driver) {
+ err("vhci_driver allocation failed");
+ goto err;
+ }
+
+ vhci_driver->nports = nports;
+ vhci_driver->hc_device = hc_device;
+ vhci_driver->ncontrollers = get_ncontrollers();
+ dbg("available controllers: %d", vhci_driver->ncontrollers);
+
+ if (vhci_driver->ncontrollers <=0) {
+ err("no available usb controllers");
+ goto err;
+ }
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+
+err:
+ udev_device_unref(hc_device);
+
+ if (vhci_driver)
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+
+ udev_unref(udev_context);
+
+ return -1;
+}
+
+
+void usbip_vhci_driver_close(void)
+{
+ if (!vhci_driver)
+ return;
+
+ udev_device_unref(vhci_driver->hc_device);
+
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+
+ udev_unref(udev_context);
+}
+
+
+int usbip_vhci_refresh_device_list(void)
+{
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+err:
+ dbg("failed to refresh device list");
+ return -1;
+}
+
+
+int usbip_vhci_get_free_port(uint32_t speed)
+{
+ for (int i = 0; i < vhci_driver->nports; i++) {
+
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
+ continue;
+ break;
+ default:
+ if (vhci_driver->idev[i].hub != HUB_SPEED_HIGH)
+ continue;
+ break;
+ }
+
+ if (vhci_driver->idev[i].status == VDEV_ST_NULL)
+ return vhci_driver->idev[i].port;
+ }
+
+ return -1;
+}
+
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed) {
+ char buff[200]; /* what size should be ? */
+ char attach_attr_path[SYSFS_PATH_MAX];
+ char attr_attach[] = "attach";
+ const char *path;
+ int ret;
+
+ snprintf(buff, sizeof(buff), "%u %d %u %u",
+ port, sockfd, devid, speed);
+ dbg("writing: %s", buff);
+
+ path = udev_device_get_syspath(vhci_driver->hc_device);
+ snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
+ path, attr_attach);
+ dbg("attach attribute path: %s", attach_attr_path);
+
+ ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
+ if (ret < 0) {
+ dbg("write_sysfs_attribute failed");
+ return -1;
+ }
+
+ dbg("attached port: %d", port);
+
+ return 0;
+}
+
+static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
+{
+ return (busnum << 16) | devnum;
+}
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed)
+{
+ int devid = get_devid(busnum, devnum);
+
+ return usbip_vhci_attach_device2(port, sockfd, devid, speed);
+}
+
+int usbip_vhci_detach_device(uint8_t port)
+{
+ char detach_attr_path[SYSFS_PATH_MAX];
+ char attr_detach[] = "detach";
+ char buff[200]; /* what size should be ? */
+ const char *path;
+ int ret;
+
+ snprintf(buff, sizeof(buff), "%u", port);
+ dbg("writing: %s", buff);
+
+ path = udev_device_get_syspath(vhci_driver->hc_device);
+ snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
+ path, attr_detach);
+ dbg("detach attribute path: %s", detach_attr_path);
+
+ ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
+ if (ret < 0) {
+ dbg("write_sysfs_attribute failed");
+ return -1;
+ }
+
+ dbg("detached port: %d", port);
+
+ return 0;
+}
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
+{
+ char product_name[100];
+ char host[NI_MAXHOST] = "unknown host";
+ char serv[NI_MAXSERV] = "unknown port";
+ char remote_busid[SYSFS_BUS_ID_SIZE];
+ int ret;
+ int read_record_error = 0;
+
+ if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
+ return 0;
+
+ ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
+ remote_busid);
+ if (ret) {
+ err("read_record");
+ read_record_error = 1;
+ }
+
+ printf("Port %02d: <%s> at %s\n", idev->port,
+ usbip_status_string(idev->status),
+ usbip_speed_string(idev->udev.speed));
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ idev->udev.idVendor, idev->udev.idProduct);
+
+ printf(" %s\n", product_name);
+
+ if (!read_record_error) {
+ printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
+ host, serv, remote_busid);
+ printf("%10s -> remote bus/dev %03d/%03d\n", " ",
+ idev->busnum, idev->devnum);
+ } else {
+ printf("%10s -> unknown host, remote port and remote busid\n",
+ idev->udev.busid);
+ printf("%10s -> remote bus/dev %03d/%03d\n", " ",
+ idev->busnum, idev->devnum);
+ }
+
+ return 0;
+}
diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h
new file mode 100644
index 000000000..6c9aca216
--- /dev/null
+++ b/tools/usb/usbip/libsrc/vhci_driver.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __VHCI_DRIVER_H
+#define __VHCI_DRIVER_H
+
+#include <libudev.h>
+#include <stdint.h>
+
+#include "usbip_common.h"
+
+#define USBIP_VHCI_BUS_TYPE "platform"
+#define USBIP_VHCI_DEVICE_NAME "vhci_hcd.0"
+
+enum hub_speed {
+ HUB_SPEED_HIGH = 0,
+ HUB_SPEED_SUPER,
+};
+
+struct usbip_imported_device {
+ enum hub_speed hub;
+ uint8_t port;
+ uint32_t status;
+
+ uint32_t devid;
+
+ uint8_t busnum;
+ uint8_t devnum;
+
+ /* usbip_class_device list */
+ struct usbip_usb_device udev;
+};
+
+struct usbip_vhci_driver {
+
+ /* /sys/devices/platform/vhci_hcd */
+ struct udev_device *hc_device;
+
+ int ncontrollers;
+ int nports;
+ struct usbip_imported_device idev[];
+};
+
+
+extern struct usbip_vhci_driver *vhci_driver;
+
+int usbip_vhci_driver_open(void);
+void usbip_vhci_driver_close(void);
+
+int usbip_vhci_refresh_device_list(void);
+
+
+int usbip_vhci_get_free_port(uint32_t speed);
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed);
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed);
+
+int usbip_vhci_detach_device(uint8_t port);
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
+
+#endif /* __VHCI_DRIVER_H */
diff --git a/tools/usb/usbip/src/Makefile.am b/tools/usb/usbip/src/Makefile.am
new file mode 100644
index 000000000..e26f39e05
--- /dev/null
+++ b/tools/usb/usbip/src/Makefile.am
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
+AM_CFLAGS = @EXTRA_CFLAGS@
+LDADD = $(top_builddir)/libsrc/libusbip.la
+
+sbin_PROGRAMS := usbip usbipd
+
+usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
+ usbip_attach.c usbip_detach.c usbip_list.c \
+ usbip_bind.c usbip_unbind.c usbip_port.c
+
+usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
diff --git a/tools/usb/usbip/src/usbip.c b/tools/usb/usbip/src/usbip.c
new file mode 100644
index 000000000..f7c7220d9
--- /dev/null
+++ b/tools/usb/usbip/src/usbip.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * command structure borrowed from udev
+ * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
+ *
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <getopt.h>
+#include <syslog.h>
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static int usbip_help(int argc, char *argv[]);
+static int usbip_version(int argc, char *argv[]);
+
+static const char usbip_version_string[] = PACKAGE_STRING;
+
+static const char usbip_usage_string[] =
+ "usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
+ " [help] <command> <args>\n";
+
+static void usbip_usage(void)
+{
+ printf("usage: %s", usbip_usage_string);
+}
+
+struct command {
+ const char *name;
+ int (*fn)(int argc, char *argv[]);
+ const char *help;
+ void (*usage)(void);
+};
+
+static const struct command cmds[] = {
+ {
+ .name = "help",
+ .fn = usbip_help,
+ .help = NULL,
+ .usage = NULL
+ },
+ {
+ .name = "version",
+ .fn = usbip_version,
+ .help = NULL,
+ .usage = NULL
+ },
+ {
+ .name = "attach",
+ .fn = usbip_attach,
+ .help = "Attach a remote USB device",
+ .usage = usbip_attach_usage
+ },
+ {
+ .name = "detach",
+ .fn = usbip_detach,
+ .help = "Detach a remote USB device",
+ .usage = usbip_detach_usage
+ },
+ {
+ .name = "list",
+ .fn = usbip_list,
+ .help = "List exportable or local USB devices",
+ .usage = usbip_list_usage
+ },
+ {
+ .name = "bind",
+ .fn = usbip_bind,
+ .help = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
+ .usage = usbip_bind_usage
+ },
+ {
+ .name = "unbind",
+ .fn = usbip_unbind,
+ .help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
+ .usage = usbip_unbind_usage
+ },
+ {
+ .name = "port",
+ .fn = usbip_port_show,
+ .help = "Show imported USB devices",
+ .usage = NULL
+ },
+ { NULL, NULL, NULL, NULL }
+};
+
+static int usbip_help(int argc, char *argv[])
+{
+ const struct command *cmd;
+ int i;
+ int ret = 0;
+
+ if (argc > 1 && argv++) {
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
+ cmds[i].usage();
+ goto done;
+ }
+ ret = -1;
+ }
+
+ usbip_usage();
+ printf("\n");
+ for (cmd = cmds; cmd->name != NULL; cmd++)
+ if (cmd->help != NULL)
+ printf(" %-10s %s\n", cmd->name, cmd->help);
+ printf("\n");
+done:
+ return ret;
+}
+
+static int usbip_version(int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+
+ printf(PROGNAME " (%s)\n", usbip_version_string);
+ return 0;
+}
+
+static int run_command(const struct command *cmd, int argc, char *argv[])
+{
+ dbg("running command: `%s'", cmd->name);
+ return cmd->fn(argc, argv);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "log", no_argument, NULL, 'l' },
+ { "tcp-port", required_argument, NULL, 't' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ char *cmd;
+ int opt;
+ int i, rc = -1;
+
+ usbip_use_stderr = 1;
+ opterr = 0;
+ for (;;) {
+ opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'd':
+ usbip_use_debug = 1;
+ break;
+ case 'l':
+ usbip_use_syslog = 1;
+ openlog("", LOG_PID, LOG_USER);
+ break;
+ case 't':
+ usbip_setup_port_number(optarg);
+ break;
+ case '?':
+ printf("usbip: invalid option\n");
+ /* Terminate after printing error */
+ /* FALLTHRU */
+ default:
+ usbip_usage();
+ goto out;
+ }
+ }
+
+ cmd = argv[optind];
+ if (cmd) {
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (!strcmp(cmds[i].name, cmd)) {
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+ rc = run_command(&cmds[i], argc, argv);
+ goto out;
+ }
+ }
+
+ /* invalid command */
+ usbip_help(0, NULL);
+out:
+ return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/tools/usb/usbip/src/usbip.h b/tools/usb/usbip/src/usbip.h
new file mode 100644
index 000000000..e31779290
--- /dev/null
+++ b/tools/usb/usbip/src/usbip.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_H
+#define __USBIP_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+/* usbip commands */
+int usbip_attach(int argc, char *argv[]);
+int usbip_detach(int argc, char *argv[]);
+int usbip_list(int argc, char *argv[]);
+int usbip_bind(int argc, char *argv[]);
+int usbip_unbind(int argc, char *argv[]);
+int usbip_port_show(int argc, char *argv[]);
+
+void usbip_attach_usage(void);
+void usbip_detach_usage(void);
+void usbip_list_usage(void);
+void usbip_bind_usage(void);
+void usbip_unbind_usage(void);
+
+#endif /* __USBIP_H */
diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c
new file mode 100644
index 000000000..531a41553
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_attach.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ */
+
+#include <sys/stat.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_attach_usage_string[] =
+ "usbip attach <args>\n"
+ " -r, --remote=<host> The machine with exported USB devices\n"
+ " -b, --busid=<busid> Busid of the device on <host>\n"
+ " -d, --device=<devid> Id of the virtual UDC on <host>\n";
+
+void usbip_attach_usage(void)
+{
+ printf("usage: %s", usbip_attach_usage_string);
+}
+
+#define MAX_BUFF 100
+static int record_connection(char *host, char *port, char *busid, int rhport)
+{
+ int fd;
+ char path[PATH_MAX+1];
+ char buff[MAX_BUFF+1];
+ int ret;
+
+ ret = mkdir(VHCI_STATE_PATH, 0700);
+ if (ret < 0) {
+ /* if VHCI_STATE_PATH exists, then it better be a directory */
+ if (errno == EEXIST) {
+ struct stat s;
+
+ ret = stat(VHCI_STATE_PATH, &s);
+ if (ret < 0)
+ return -1;
+ if (!(s.st_mode & S_IFDIR))
+ return -1;
+ } else
+ return -1;
+ }
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
+ if (fd < 0)
+ return -1;
+
+ snprintf(buff, MAX_BUFF, "%s %s %s\n",
+ host, port, busid);
+
+ ret = write(fd, buff, strlen(buff));
+ if (ret != (ssize_t) strlen(buff)) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int import_device(int sockfd, struct usbip_usb_device *udev)
+{
+ int rc;
+ int port;
+ uint32_t speed = udev->speed;
+
+ rc = usbip_vhci_driver_open();
+ if (rc < 0) {
+ err("open vhci_driver (is vhci_hcd loaded?)");
+ goto err_out;
+ }
+
+ do {
+ port = usbip_vhci_get_free_port(speed);
+ if (port < 0) {
+ err("no free port");
+ goto err_driver_close;
+ }
+
+ dbg("got free port %d", port);
+
+ rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+ udev->devnum, udev->speed);
+ if (rc < 0 && errno != EBUSY) {
+ err("import device");
+ goto err_driver_close;
+ }
+ } while (rc < 0);
+
+ usbip_vhci_driver_close();
+
+ return port;
+
+err_driver_close:
+ usbip_vhci_driver_close();
+err_out:
+ return -1;
+}
+
+static int query_import_device(int sockfd, char *busid)
+{
+ int rc;
+ struct op_import_request request;
+ struct op_import_reply reply;
+ uint16_t code = OP_REP_IMPORT;
+ int status;
+
+ memset(&request, 0, sizeof(request));
+ memset(&reply, 0, sizeof(reply));
+
+ /* send a request */
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+ if (rc < 0) {
+ err("send op_common");
+ return -1;
+ }
+
+ strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
+
+ PACK_OP_IMPORT_REQUEST(0, &request);
+
+ rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+ if (rc < 0) {
+ err("send op_import_request");
+ return -1;
+ }
+
+ /* receive a reply */
+ rc = usbip_net_recv_op_common(sockfd, &code, &status);
+ if (rc < 0) {
+ err("Attach Request for %s failed - %s\n",
+ busid, usbip_op_common_status_string(status));
+ return -1;
+ }
+
+ rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
+ if (rc < 0) {
+ err("recv op_import_reply");
+ return -1;
+ }
+
+ PACK_OP_IMPORT_REPLY(0, &reply);
+
+ /* check the reply */
+ if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
+ err("recv different busid %s", reply.udev.busid);
+ return -1;
+ }
+
+ /* import a device */
+ return import_device(sockfd, &reply.udev);
+}
+
+static int attach_device(char *host, char *busid)
+{
+ int sockfd;
+ int rc;
+ int rhport;
+
+ sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+ if (sockfd < 0) {
+ err("tcp connect");
+ return -1;
+ }
+
+ rhport = query_import_device(sockfd, busid);
+ if (rhport < 0)
+ return -1;
+
+ close(sockfd);
+
+ rc = record_connection(host, usbip_port_string, busid, rhport);
+ if (rc < 0) {
+ err("record connection");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usbip_attach(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "remote", required_argument, NULL, 'r' },
+ { "busid", required_argument, NULL, 'b' },
+ { "device", required_argument, NULL, 'd' },
+ { NULL, 0, NULL, 0 }
+ };
+ char *host = NULL;
+ char *busid = NULL;
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "d:r:b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'r':
+ host = optarg;
+ break;
+ case 'd':
+ case 'b':
+ busid = optarg;
+ break;
+ default:
+ goto err_out;
+ }
+ }
+
+ if (!host || !busid)
+ goto err_out;
+
+ ret = attach_device(host, busid);
+ goto out;
+
+err_out:
+ usbip_attach_usage();
+out:
+ return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c
new file mode 100644
index 000000000..f1cf9225a
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_bind.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "usbip.h"
+#include "sysfs_utils.h"
+
+enum unbind_status {
+ UNBIND_ST_OK,
+ UNBIND_ST_USBIP_HOST,
+ UNBIND_ST_FAILED
+};
+
+static const char usbip_bind_usage_string[] =
+ "usbip bind <args>\n"
+ " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
+ "on <busid>\n";
+
+void usbip_bind_usage(void)
+{
+ printf("usage: %s", usbip_bind_usage_string);
+}
+
+/* call at unbound state */
+static int bind_usbip(char *busid)
+{
+ char attr_name[] = "bind";
+ char bind_attr_path[SYSFS_PATH_MAX];
+ int rc = -1;
+
+ snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
+ SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
+
+ rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error binding device %s to driver: %s", busid,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* buggy driver may cause dead lock */
+static int unbind_other(char *busid)
+{
+ enum unbind_status status = UNBIND_ST_OK;
+
+ char attr_name[] = "unbind";
+ char unbind_attr_path[SYSFS_PATH_MAX];
+ int rc = -1;
+
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *driver;
+ const char *bDevClass;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Get the device. */
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ dbg("unable to find device with bus ID %s", busid);
+ goto err_close_busid_dev;
+ }
+
+ /* Check what kind of device it is. */
+ bDevClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
+ if (!bDevClass) {
+ dbg("unable to get bDevClass device attribute");
+ goto err_close_busid_dev;
+ }
+
+ if (!strncmp(bDevClass, "09", strlen(bDevClass))) {
+ dbg("skip unbinding of hub");
+ goto err_close_busid_dev;
+ }
+
+ /* Get the device driver. */
+ driver = udev_device_get_driver(dev);
+ if (!driver) {
+ /* No driver bound to this device. */
+ goto out;
+ }
+
+ if (!strncmp(USBIP_HOST_DRV_NAME, driver,
+ strlen(USBIP_HOST_DRV_NAME))) {
+ /* Already bound to usbip-host. */
+ status = UNBIND_ST_USBIP_HOST;
+ goto out;
+ }
+
+ /* Unbind device from driver. */
+ snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
+ SYSFS_DRIVERS_NAME, driver, attr_name);
+
+ rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error unbinding device %s from driver", busid);
+ goto err_close_busid_dev;
+ }
+
+ goto out;
+
+err_close_busid_dev:
+ status = UNBIND_ST_FAILED;
+out:
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return status;
+}
+
+static int bind_device(char *busid)
+{
+ int rc;
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *devpath;
+
+ /* Check whether the device with this bus ID exists. */
+ udev = udev_new();
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ err("device with the specified bus ID does not exist");
+ return -1;
+ }
+ devpath = udev_device_get_devpath(dev);
+ udev_unref(udev);
+
+ /* If the device is already attached to vhci_hcd - bail out */
+ if (strstr(devpath, USBIP_VHCI_DRV_NAME)) {
+ err("bind loop detected: device: %s is attached to %s\n",
+ devpath, USBIP_VHCI_DRV_NAME);
+ return -1;
+ }
+
+ rc = unbind_other(busid);
+ if (rc == UNBIND_ST_FAILED) {
+ err("could not unbind driver from device on busid %s", busid);
+ return -1;
+ } else if (rc == UNBIND_ST_USBIP_HOST) {
+ err("device on busid %s is already bound to %s", busid,
+ USBIP_HOST_DRV_NAME);
+ return -1;
+ }
+
+ rc = modify_match_busid(busid, 1);
+ if (rc < 0) {
+ err("unable to bind device on %s", busid);
+ return -1;
+ }
+
+ rc = bind_usbip(busid);
+ if (rc < 0) {
+ err("could not bind device to %s", USBIP_HOST_DRV_NAME);
+ modify_match_busid(busid, 0);
+ return -1;
+ }
+
+ info("bind device on busid %s: complete", busid);
+
+ return 0;
+}
+
+int usbip_bind(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ ret = bind_device(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_bind_usage();
+out:
+ return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c
new file mode 100644
index 000000000..6b78d4a81
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_detach.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_detach_usage_string[] =
+ "usbip detach <args>\n"
+ " -p, --port=<port> " USBIP_VHCI_DRV_NAME
+ " port the device is on\n";
+
+void usbip_detach_usage(void)
+{
+ printf("usage: %s", usbip_detach_usage_string);
+}
+
+static int detach_port(char *port)
+{
+ int ret = 0;
+ uint8_t portnum;
+ char path[PATH_MAX+1];
+ int i;
+ struct usbip_imported_device *idev;
+ int found = 0;
+
+ unsigned int port_len = strlen(port);
+
+ for (unsigned int i = 0; i < port_len; i++)
+ if (!isdigit(port[i])) {
+ err("invalid port %s", port);
+ return -1;
+ }
+
+ portnum = atoi(port);
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver (is vhci_hcd loaded?)");
+ return -1;
+ }
+
+ /* check for invalid port */
+ for (i = 0; i < vhci_driver->nports; i++) {
+ idev = &vhci_driver->idev[i];
+
+ if (idev->port == portnum) {
+ found = 1;
+ if (idev->status != VDEV_ST_NULL)
+ break;
+ info("Port %d is already detached!\n", idev->port);
+ goto call_driver_close;
+ }
+ }
+
+ if (!found) {
+ ret = -1;
+ err("Invalid port %s > maxports %d",
+ port, vhci_driver->nports);
+ goto call_driver_close;
+ }
+
+ /* remove the port state file */
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
+
+ remove(path);
+ rmdir(VHCI_STATE_PATH);
+
+ ret = usbip_vhci_detach_device(portnum);
+ if (ret < 0) {
+ ret = -1;
+ err("Port %d detach request failed!\n", portnum);
+ goto call_driver_close;
+ }
+ info("Port %d is now detached!\n", portnum);
+
+call_driver_close:
+ usbip_vhci_driver_close();
+
+ return ret;
+}
+
+int usbip_detach(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "port", required_argument, NULL, 'p' },
+ { NULL, 0, NULL, 0 }
+ };
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "p:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'p':
+ ret = detach_port(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_detach_usage();
+out:
+ return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c
new file mode 100644
index 000000000..3d810bcca
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_list.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ */
+
+#include <sys/types.h>
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <dirent.h>
+
+#include <linux/usb/ch9.h>
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_list_usage_string[] =
+ "usbip list [-p|--parsable] <args>\n"
+ " -p, --parsable Parsable list format\n"
+ " -r, --remote=<host> List the exportable USB devices on <host>\n"
+ " -l, --local List the local USB devices\n"
+ " -d, --device List the local USB gadgets bound to usbip-vudc\n";
+
+void usbip_list_usage(void)
+{
+ printf("usage: %s", usbip_list_usage_string);
+}
+
+static int get_exported_devices(char *host, int sockfd)
+{
+ char product_name[100];
+ char class_name[100];
+ struct op_devlist_reply reply;
+ uint16_t code = OP_REP_DEVLIST;
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uintf;
+ unsigned int i;
+ int rc, j;
+ int status;
+
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed");
+ return -1;
+ }
+
+ rc = usbip_net_recv_op_common(sockfd, &code, &status);
+ if (rc < 0) {
+ err("Exported Device List Request failed - %s\n",
+ usbip_op_common_status_string(status));
+ return -1;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
+ if (rc < 0) {
+ dbg("usbip_net_recv_op_devlist failed");
+ return -1;
+ }
+ PACK_OP_DEVLIST_REPLY(0, &reply);
+ dbg("exportable devices: %d\n", reply.ndev);
+
+ if (reply.ndev == 0) {
+ info("no exportable devices found on %s", host);
+ return 0;
+ }
+
+ printf("Exportable USB devices\n");
+ printf("======================\n");
+ printf(" - %s\n", host);
+
+ for (i = 0; i < reply.ndev; i++) {
+ memset(&udev, 0, sizeof(udev));
+ rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
+ return -1;
+ }
+ usbip_net_pack_usb_device(0, &udev);
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ udev.idVendor, udev.idProduct);
+ usbip_names_get_class(class_name, sizeof(class_name),
+ udev.bDeviceClass, udev.bDeviceSubClass,
+ udev.bDeviceProtocol);
+ printf("%11s: %s\n", udev.busid, product_name);
+ printf("%11s: %s\n", "", udev.path);
+ printf("%11s: %s\n", "", class_name);
+
+ for (j = 0; j < udev.bNumInterfaces; j++) {
+ rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
+ if (rc < 0) {
+ err("usbip_net_recv failed: usbip_usb_intf[%d]",
+ j);
+
+ return -1;
+ }
+ usbip_net_pack_usb_interface(0, &uintf);
+
+ usbip_names_get_class(class_name, sizeof(class_name),
+ uintf.bInterfaceClass,
+ uintf.bInterfaceSubClass,
+ uintf.bInterfaceProtocol);
+ printf("%11s: %2d - %s\n", "", j, class_name);
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int list_exported_devices(char *host)
+{
+ int rc;
+ int sockfd;
+
+ sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+ if (sockfd < 0) {
+ err("could not connect to %s:%s: %s", host,
+ usbip_port_string, gai_strerror(sockfd));
+ return -1;
+ }
+ dbg("connected to %s:%s", host, usbip_port_string);
+
+ rc = get_exported_devices(host, sockfd);
+ if (rc < 0) {
+ err("failed to get device list from %s", host);
+ return -1;
+ }
+
+ close(sockfd);
+
+ return 0;
+}
+
+static void print_device(const char *busid, const char *vendor,
+ const char *product, bool parsable)
+{
+ if (parsable)
+ printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product);
+ else
+ printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product);
+}
+
+static void print_product_name(char *product_name, bool parsable)
+{
+ if (!parsable)
+ printf(" %s\n", product_name);
+}
+
+static int list_devices(bool parsable)
+{
+ struct udev *udev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+ const char *idVendor;
+ const char *idProduct;
+ const char *bConfValue;
+ const char *bNumIntfs;
+ const char *busid;
+ char product_name[128];
+ int ret = -1;
+ const char *devpath;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Create libudev device enumeration. */
+ enumerate = udev_enumerate_new(udev);
+
+ /* Take only USB devices that are not hubs and do not have
+ * the bInterfaceNumber attribute, i.e. are not interfaces.
+ */
+ udev_enumerate_add_match_subsystem(enumerate, "usb");
+ udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09");
+ udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL);
+ udev_enumerate_scan_devices(enumerate);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* Show information about each device. */
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+
+ /* Ignore devices attached to vhci_hcd */
+ devpath = udev_device_get_devpath(dev);
+ if (strstr(devpath, USBIP_VHCI_DRV_NAME)) {
+ dbg("Skip the device %s already attached to %s\n",
+ devpath, USBIP_VHCI_DRV_NAME);
+ continue;
+ }
+
+ /* Get device information. */
+ idVendor = udev_device_get_sysattr_value(dev, "idVendor");
+ idProduct = udev_device_get_sysattr_value(dev, "idProduct");
+ bConfValue = udev_device_get_sysattr_value(dev,
+ "bConfigurationValue");
+ bNumIntfs = udev_device_get_sysattr_value(dev,
+ "bNumInterfaces");
+ busid = udev_device_get_sysname(dev);
+ if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) {
+ err("problem getting device attributes: %s",
+ strerror(errno));
+ goto err_out;
+ }
+
+ /* Get product name. */
+ usbip_names_get_product(product_name, sizeof(product_name),
+ strtol(idVendor, NULL, 16),
+ strtol(idProduct, NULL, 16));
+
+ /* Print information. */
+ print_device(busid, idVendor, idProduct, parsable);
+ print_product_name(product_name, parsable);
+
+ printf("\n");
+
+ udev_device_unref(dev);
+ }
+
+ ret = 0;
+
+err_out:
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return ret;
+}
+
+static int list_gadget_devices(bool parsable)
+{
+ int ret = -1;
+ struct udev *udev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+ const char *driver;
+
+ const struct usb_device_descriptor *d_desc;
+ const char *descriptors;
+ char product_name[128];
+
+ uint16_t idVendor;
+ char idVendor_buf[8];
+ uint16_t idProduct;
+ char idProduct_buf[8];
+ const char *busid;
+
+ udev = udev_new();
+ enumerate = udev_enumerate_new(udev);
+
+ udev_enumerate_add_match_subsystem(enumerate, "platform");
+
+ udev_enumerate_scan_devices(enumerate);
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+
+ driver = udev_device_get_driver(dev);
+ /* We only have mechanism to enumerate gadgets bound to vudc */
+ if (driver == NULL || strcmp(driver, USBIP_DEVICE_DRV_NAME))
+ continue;
+
+ /* Get device information. */
+ descriptors = udev_device_get_sysattr_value(dev,
+ VUDC_DEVICE_DESCR_FILE);
+
+ if (!descriptors) {
+ err("problem getting device attributes: %s",
+ strerror(errno));
+ goto err_out;
+ }
+
+ d_desc = (const struct usb_device_descriptor *) descriptors;
+
+ idVendor = le16toh(d_desc->idVendor);
+ sprintf(idVendor_buf, "0x%4x", idVendor);
+ idProduct = le16toh(d_desc->idProduct);
+ sprintf(idProduct_buf, "0x%4x", idVendor);
+ busid = udev_device_get_sysname(dev);
+
+ /* Get product name. */
+ usbip_names_get_product(product_name, sizeof(product_name),
+ le16toh(idVendor),
+ le16toh(idProduct));
+
+ /* Print information. */
+ print_device(busid, idVendor_buf, idProduct_buf, parsable);
+ print_product_name(product_name, parsable);
+
+ printf("\n");
+
+ udev_device_unref(dev);
+ }
+ ret = 0;
+
+err_out:
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return ret;
+}
+
+int usbip_list(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "parsable", no_argument, NULL, 'p' },
+ { "remote", required_argument, NULL, 'r' },
+ { "local", no_argument, NULL, 'l' },
+ { "device", no_argument, NULL, 'd' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ bool parsable = false;
+ int opt;
+ int ret = -1;
+
+ if (usbip_names_init(USBIDS_FILE))
+ err("failed to open %s", USBIDS_FILE);
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "pr:ld", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'p':
+ parsable = true;
+ break;
+ case 'r':
+ ret = list_exported_devices(optarg);
+ goto out;
+ case 'l':
+ ret = list_devices(parsable);
+ goto out;
+ case 'd':
+ ret = list_gadget_devices(parsable);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_list_usage();
+out:
+ usbip_names_free();
+
+ return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_network.c b/tools/usb/usbip/src/usbip_network.c
new file mode 100644
index 000000000..ed4dc8c14
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_network.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+
+int usbip_port = 3240;
+char *usbip_port_string = "3240";
+
+void usbip_setup_port_number(char *arg)
+{
+ dbg("parsing port arg '%s'", arg);
+ char *end;
+ unsigned long int port = strtoul(arg, &end, 10);
+
+ if (end == arg) {
+ err("port: could not parse '%s' as a decimal integer", arg);
+ return;
+ }
+
+ if (*end != '\0') {
+ err("port: garbage at end of '%s'", arg);
+ return;
+ }
+
+ if (port > UINT16_MAX) {
+ err("port: %s too high (max=%d)",
+ arg, UINT16_MAX);
+ return;
+ }
+
+ usbip_port = port;
+ usbip_port_string = arg;
+ info("using port %d (\"%s\")", usbip_port, usbip_port_string);
+}
+
+uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num)
+{
+ uint32_t i;
+
+ if (pack)
+ i = htonl(num);
+ else
+ i = ntohl(num);
+
+ return i;
+}
+
+uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num)
+{
+ uint16_t i;
+
+ if (pack)
+ i = htons(num);
+ else
+ i = ntohs(num);
+
+ return i;
+}
+
+void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev)
+{
+ udev->busnum = usbip_net_pack_uint32_t(pack, udev->busnum);
+ udev->devnum = usbip_net_pack_uint32_t(pack, udev->devnum);
+ udev->speed = usbip_net_pack_uint32_t(pack, udev->speed);
+
+ udev->idVendor = usbip_net_pack_uint16_t(pack, udev->idVendor);
+ udev->idProduct = usbip_net_pack_uint16_t(pack, udev->idProduct);
+ udev->bcdDevice = usbip_net_pack_uint16_t(pack, udev->bcdDevice);
+}
+
+void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
+ struct usbip_usb_interface *udev
+ __attribute__((unused)))
+{
+ /* uint8_t members need nothing */
+}
+
+static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
+ int sending)
+{
+ ssize_t nbytes;
+ ssize_t total = 0;
+
+ if (!bufflen)
+ return 0;
+
+ do {
+ if (sending)
+ nbytes = send(sockfd, buff, bufflen, 0);
+ else
+ nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
+
+ if (nbytes <= 0)
+ return -1;
+
+ buff = (void *)((intptr_t) buff + nbytes);
+ bufflen -= nbytes;
+ total += nbytes;
+
+ } while (bufflen > 0);
+
+ return total;
+}
+
+ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
+{
+ return usbip_net_xmit(sockfd, buff, bufflen, 0);
+}
+
+ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
+{
+ return usbip_net_xmit(sockfd, buff, bufflen, 1);
+}
+
+static inline void usbip_net_pack_op_common(int pack,
+ struct op_common *op_common)
+{
+ op_common->version = usbip_net_pack_uint16_t(pack, op_common->version);
+ op_common->code = usbip_net_pack_uint16_t(pack, op_common->code);
+ op_common->status = usbip_net_pack_uint32_t(pack, op_common->status);
+}
+
+int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
+{
+ struct op_common op_common;
+ int rc;
+
+ memset(&op_common, 0, sizeof(op_common));
+
+ op_common.version = USBIP_VERSION;
+ op_common.code = code;
+ op_common.status = status;
+
+ usbip_net_pack_op_common(1, &op_common);
+
+ rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: %d", rc);
+ return -1;
+ }
+
+ return 0;
+}
+
+int usbip_net_recv_op_common(int sockfd, uint16_t *code, int *status)
+{
+ struct op_common op_common;
+ int rc;
+
+ memset(&op_common, 0, sizeof(op_common));
+
+ rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: %d", rc);
+ goto err;
+ }
+
+ usbip_net_pack_op_common(0, &op_common);
+
+ if (op_common.version != USBIP_VERSION) {
+ err("USBIP Kernel and tool version mismatch: %d %d:",
+ op_common.version, USBIP_VERSION);
+ goto err;
+ }
+
+ switch (*code) {
+ case OP_UNSPEC:
+ break;
+ default:
+ if (op_common.code != *code) {
+ dbg("unexpected pdu %#0x for %#0x", op_common.code,
+ *code);
+ /* return error status */
+ *status = ST_ERROR;
+ goto err;
+ }
+ }
+
+ *status = op_common.status;
+
+ if (op_common.status != ST_OK) {
+ dbg("request failed at peer: %d", op_common.status);
+ goto err;
+ }
+
+ *code = op_common.code;
+
+ return 0;
+err:
+ return -1;
+}
+
+int usbip_net_set_reuseaddr(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: SO_REUSEADDR");
+
+ return ret;
+}
+
+int usbip_net_set_nodelay(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: TCP_NODELAY");
+
+ return ret;
+}
+
+int usbip_net_set_keepalive(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: SO_KEEPALIVE");
+
+ return ret;
+}
+
+int usbip_net_set_v6only(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: IPV6_V6ONLY");
+
+ return ret;
+}
+
+/*
+ * IPv6 Ready
+ */
+int usbip_net_tcp_connect(char *hostname, char *service)
+{
+ struct addrinfo hints, *res, *rp;
+ int sockfd;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* get all possible addresses */
+ ret = getaddrinfo(hostname, service, &hints, &res);
+ if (ret < 0) {
+ dbg("getaddrinfo: %s service %s: %s", hostname, service,
+ gai_strerror(ret));
+ return ret;
+ }
+
+ /* try the addresses */
+ for (rp = res; rp; rp = rp->ai_next) {
+ sockfd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (sockfd < 0)
+ continue;
+
+ /* should set TCP_NODELAY for usbip */
+ usbip_net_set_nodelay(sockfd);
+ /* TODO: write code for heartbeat */
+ usbip_net_set_keepalive(sockfd);
+
+ if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+
+ close(sockfd);
+ }
+
+ freeaddrinfo(res);
+
+ if (!rp)
+ return EAI_SYSTEM;
+
+ return sockfd;
+}
diff --git a/tools/usb/usbip/src/usbip_network.h b/tools/usb/usbip/src/usbip_network.h
new file mode 100644
index 000000000..83b4c5344
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_network.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_NETWORK_H
+#define __USBIP_NETWORK_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+extern int usbip_port;
+extern char *usbip_port_string;
+void usbip_setup_port_number(char *arg);
+
+/* ---------------------------------------------------------------------- */
+/* Common header for all the kinds of PDUs. */
+struct op_common {
+ uint16_t version;
+
+#define OP_REQUEST (0x80 << 8)
+#define OP_REPLY (0x00 << 8)
+ uint16_t code;
+
+ /* status codes defined in usbip_common.h */
+ uint32_t status; /* op_code status (for reply) */
+
+} __attribute__((packed));
+
+/* ---------------------------------------------------------------------- */
+/* Dummy Code */
+#define OP_UNSPEC 0x00
+#define OP_REQ_UNSPEC OP_UNSPEC
+#define OP_REP_UNSPEC OP_UNSPEC
+
+/* ---------------------------------------------------------------------- */
+/* Retrieve USB device information. (still not used) */
+#define OP_DEVINFO 0x02
+#define OP_REQ_DEVINFO (OP_REQUEST | OP_DEVINFO)
+#define OP_REP_DEVINFO (OP_REPLY | OP_DEVINFO)
+
+struct op_devinfo_request {
+ char busid[SYSFS_BUS_ID_SIZE];
+} __attribute__((packed));
+
+struct op_devinfo_reply {
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+/* ---------------------------------------------------------------------- */
+/* Import a remote USB device. */
+#define OP_IMPORT 0x03
+#define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT)
+#define OP_REP_IMPORT (OP_REPLY | OP_IMPORT)
+
+struct op_import_request {
+ char busid[SYSFS_BUS_ID_SIZE];
+} __attribute__((packed));
+
+struct op_import_reply {
+ struct usbip_usb_device udev;
+// struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+#define PACK_OP_IMPORT_REQUEST(pack, request) do {\
+} while (0)
+
+#define PACK_OP_IMPORT_REPLY(pack, reply) do {\
+ usbip_net_pack_usb_device(pack, &(reply)->udev);\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Export a USB device to a remote host. */
+#define OP_EXPORT 0x06
+#define OP_REQ_EXPORT (OP_REQUEST | OP_EXPORT)
+#define OP_REP_EXPORT (OP_REPLY | OP_EXPORT)
+
+struct op_export_request {
+ struct usbip_usb_device udev;
+} __attribute__((packed));
+
+struct op_export_reply {
+ int returncode;
+} __attribute__((packed));
+
+
+#define PACK_OP_EXPORT_REQUEST(pack, request) do {\
+ usbip_net_pack_usb_device(pack, &(request)->udev);\
+} while (0)
+
+#define PACK_OP_EXPORT_REPLY(pack, reply) do {\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* un-Export a USB device from a remote host. */
+#define OP_UNEXPORT 0x07
+#define OP_REQ_UNEXPORT (OP_REQUEST | OP_UNEXPORT)
+#define OP_REP_UNEXPORT (OP_REPLY | OP_UNEXPORT)
+
+struct op_unexport_request {
+ struct usbip_usb_device udev;
+} __attribute__((packed));
+
+struct op_unexport_reply {
+ int returncode;
+} __attribute__((packed));
+
+#define PACK_OP_UNEXPORT_REQUEST(pack, request) do {\
+ usbip_net_pack_usb_device(pack, &(request)->udev);\
+} while (0)
+
+#define PACK_OP_UNEXPORT_REPLY(pack, reply) do {\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Negotiate IPSec encryption key. (still not used) */
+#define OP_CRYPKEY 0x04
+#define OP_REQ_CRYPKEY (OP_REQUEST | OP_CRYPKEY)
+#define OP_REP_CRYPKEY (OP_REPLY | OP_CRYPKEY)
+
+struct op_crypkey_request {
+ /* 128bit key */
+ uint32_t key[4];
+} __attribute__((packed));
+
+struct op_crypkey_reply {
+ uint32_t __reserved;
+} __attribute__((packed));
+
+
+/* ---------------------------------------------------------------------- */
+/* Retrieve the list of exported USB devices. */
+#define OP_DEVLIST 0x05
+#define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST)
+#define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST)
+
+struct op_devlist_request {
+} __attribute__((packed));
+
+struct op_devlist_reply {
+ uint32_t ndev;
+ /* followed by reply_extra[] */
+} __attribute__((packed));
+
+struct op_devlist_reply_extra {
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+#define PACK_OP_DEVLIST_REQUEST(pack, request) do {\
+} while (0)
+
+#define PACK_OP_DEVLIST_REPLY(pack, reply) do {\
+ (reply)->ndev = usbip_net_pack_uint32_t(pack, (reply)->ndev);\
+} while (0)
+
+uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num);
+uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num);
+void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
+void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
+
+ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
+ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
+int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+int usbip_net_recv_op_common(int sockfd, uint16_t *code, int *status);
+int usbip_net_set_reuseaddr(int sockfd);
+int usbip_net_set_nodelay(int sockfd);
+int usbip_net_set_keepalive(int sockfd);
+int usbip_net_set_v6only(int sockfd);
+int usbip_net_tcp_connect(char *hostname, char *port);
+
+#endif /* __USBIP_NETWORK_H */
diff --git a/tools/usb/usbip/src/usbip_port.c b/tools/usb/usbip/src/usbip_port.c
new file mode 100644
index 000000000..21a20e378
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_port.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+
+static int list_imported_devices(void)
+{
+ int i;
+ struct usbip_imported_device *idev;
+ int ret;
+
+ if (usbip_names_init(USBIDS_FILE))
+ err("failed to open %s", USBIDS_FILE);
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver (is vhci_hcd loaded?)");
+ goto err_names_free;
+ }
+
+ printf("Imported USB devices\n");
+ printf("====================\n");
+
+ for (i = 0; i < vhci_driver->nports; i++) {
+ idev = &vhci_driver->idev[i];
+
+ if (usbip_vhci_imported_device_dump(idev) < 0)
+ goto err_driver_close;
+ }
+
+ usbip_vhci_driver_close();
+ usbip_names_free();
+
+ return ret;
+
+err_driver_close:
+ usbip_vhci_driver_close();
+err_names_free:
+ usbip_names_free();
+ return -1;
+}
+
+int usbip_port_show(__attribute__((unused)) int argc,
+ __attribute__((unused)) char *argv[])
+{
+ int ret;
+
+ ret = list_imported_devices();
+ if (ret < 0)
+ err("list imported devices");
+
+ return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_unbind.c b/tools/usb/usbip/src/usbip_unbind.c
new file mode 100644
index 000000000..66a44d4a0
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_unbind.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "usbip.h"
+#include "sysfs_utils.h"
+
+static const char usbip_unbind_usage_string[] =
+ "usbip unbind <args>\n"
+ " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from "
+ "device on <busid>\n";
+
+void usbip_unbind_usage(void)
+{
+ printf("usage: %s", usbip_unbind_usage_string);
+}
+
+static int unbind_device(char *busid)
+{
+ char bus_type[] = "usb";
+ int rc, ret = -1;
+
+ char unbind_attr_name[] = "unbind";
+ char unbind_attr_path[SYSFS_PATH_MAX];
+ char rebind_attr_name[] = "rebind";
+ char rebind_attr_path[SYSFS_PATH_MAX];
+
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *driver;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Check whether the device with this bus ID exists. */
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ err("device with the specified bus ID does not exist");
+ goto err_close_udev;
+ }
+
+ /* Check whether the device is using usbip-host driver. */
+ driver = udev_device_get_driver(dev);
+ if (!driver || strcmp(driver, "usbip-host")) {
+ err("device is not bound to usbip-host driver");
+ goto err_close_udev;
+ }
+
+ /* Unbind device from driver. */
+ snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
+ USBIP_HOST_DRV_NAME, unbind_attr_name);
+
+ rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error unbinding device %s from driver", busid);
+ goto err_close_udev;
+ }
+
+ /* Notify driver of unbind. */
+ rc = modify_match_busid(busid, 0);
+ if (rc < 0) {
+ err("unable to unbind device on %s", busid);
+ goto err_close_udev;
+ }
+
+ /* Trigger new probing. */
+ snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
+ USBIP_HOST_DRV_NAME, rebind_attr_name);
+
+ rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error rebinding");
+ goto err_close_udev;
+ }
+
+ ret = 0;
+ info("unbind device on busid %s: complete", busid);
+
+err_close_udev:
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return ret;
+}
+
+int usbip_unbind(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ ret = unbind_device(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_unbind_usage();
+out:
+ return ret;
+}
diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c
new file mode 100644
index 000000000..3e22b651c
--- /dev/null
+++ b/tools/usb/usbip/src/usbipd.c
@@ -0,0 +1,686 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Samsung Electronics
+ * Igor Kotrasinski <i.kotrasinsk@samsung.com>
+ * Krzysztof Opasiak <k.opasiak@samsung.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include <getopt.h>
+#include <signal.h>
+#include <poll.h>
+
+#include "usbip_host_driver.h"
+#include "usbip_host_common.h"
+#include "usbip_device_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "list.h"
+
+#undef PROGNAME
+#define PROGNAME "usbipd"
+#define MAXSOCKFD 20
+
+#define MAIN_LOOP_TIMEOUT 10
+
+#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
+
+static const char usbip_version_string[] = PACKAGE_STRING;
+
+static const char usbipd_help_string[] =
+ "usage: usbipd [options]\n"
+ "\n"
+ " -4, --ipv4\n"
+ " Bind to IPv4. Default is both.\n"
+ "\n"
+ " -6, --ipv6\n"
+ " Bind to IPv6. Default is both.\n"
+ "\n"
+ " -e, --device\n"
+ " Run in device mode.\n"
+ " Rather than drive an attached device, create\n"
+ " a virtual UDC to bind gadgets to.\n"
+ "\n"
+ " -D, --daemon\n"
+ " Run as a daemon process.\n"
+ "\n"
+ " -d, --debug\n"
+ " Print debugging information.\n"
+ "\n"
+ " -PFILE, --pid FILE\n"
+ " Write process id to FILE.\n"
+ " If no FILE specified, use " DEFAULT_PID_FILE "\n"
+ "\n"
+ " -tPORT, --tcp-port PORT\n"
+ " Listen on TCP/IP port PORT.\n"
+ "\n"
+ " -h, --help\n"
+ " Print this help.\n"
+ "\n"
+ " -v, --version\n"
+ " Show version.\n";
+
+static struct usbip_host_driver *driver;
+
+static void usbipd_help(void)
+{
+ printf("%s\n", usbipd_help_string);
+}
+
+static int recv_request_import(int sockfd)
+{
+ struct op_import_request req;
+ struct usbip_exported_device *edev;
+ struct usbip_usb_device pdu_udev;
+ struct list_head *i;
+ int found = 0;
+ int status = ST_OK;
+ int rc;
+
+ memset(&req, 0, sizeof(req));
+
+ rc = usbip_net_recv(sockfd, &req, sizeof(req));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: import request");
+ return -1;
+ }
+ PACK_OP_IMPORT_REQUEST(0, &req);
+
+ list_for_each(i, &driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+ info("found requested device: %s", req.busid);
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ /* should set TCP_NODELAY for usbip */
+ usbip_net_set_nodelay(sockfd);
+
+ /* export device needs a TCP/IP socket descriptor */
+ status = usbip_export_device(edev, sockfd);
+ if (status < 0)
+ status = ST_NA;
+ } else {
+ info("requested device not found: %s", req.busid);
+ status = ST_NODEV;
+ }
+
+ rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, status);
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
+ return -1;
+ }
+
+ if (status) {
+ dbg("import request busid %s: failed", req.busid);
+ return -1;
+ }
+
+ memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+ usbip_net_pack_usb_device(1, &pdu_udev);
+
+ rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: devinfo");
+ return -1;
+ }
+
+ dbg("import request busid %s: complete", req.busid);
+
+ return 0;
+}
+
+static int send_reply_devlist(int connfd)
+{
+ struct usbip_exported_device *edev;
+ struct usbip_usb_device pdu_udev;
+ struct usbip_usb_interface pdu_uinf;
+ struct op_devlist_reply reply;
+ struct list_head *j;
+ int rc, i;
+
+ /*
+ * Exclude devices that are already exported to a client from
+ * the exportable device list to avoid:
+ * - import requests for devices that are exported only to
+ * fail the request.
+ * - revealing devices that are imported by a client to
+ * another client.
+ */
+
+ reply.ndev = 0;
+ /* number of exported devices */
+ list_for_each(j, &driver->edev_list) {
+ edev = list_entry(j, struct usbip_exported_device, node);
+ if (edev->status != SDEV_ST_USED)
+ reply.ndev += 1;
+ }
+ info("exportable devices: %d", reply.ndev);
+
+ rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
+ return -1;
+ }
+ PACK_OP_DEVLIST_REPLY(1, &reply);
+
+ rc = usbip_net_send(connfd, &reply, sizeof(reply));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
+ return -1;
+ }
+
+ list_for_each(j, &driver->edev_list) {
+ edev = list_entry(j, struct usbip_exported_device, node);
+ if (edev->status == SDEV_ST_USED)
+ continue;
+
+ dump_usb_device(&edev->udev);
+ memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+ usbip_net_pack_usb_device(1, &pdu_udev);
+
+ rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: pdu_udev");
+ return -1;
+ }
+
+ for (i = 0; i < edev->udev.bNumInterfaces; i++) {
+ dump_usb_interface(&edev->uinf[i]);
+ memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
+ usbip_net_pack_usb_interface(1, &pdu_uinf);
+
+ rc = usbip_net_send(connfd, &pdu_uinf,
+ sizeof(pdu_uinf));
+ if (rc < 0) {
+ err("usbip_net_send failed: pdu_uinf");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int recv_request_devlist(int connfd)
+{
+ struct op_devlist_request req;
+ int rc;
+
+ memset(&req, 0, sizeof(req));
+
+ rc = usbip_net_recv(connfd, &req, sizeof(req));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: devlist request");
+ return -1;
+ }
+
+ rc = send_reply_devlist(connfd);
+ if (rc < 0) {
+ dbg("send_reply_devlist failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int recv_pdu(int connfd)
+{
+ uint16_t code = OP_UNSPEC;
+ int ret;
+ int status;
+
+ ret = usbip_net_recv_op_common(connfd, &code, &status);
+ if (ret < 0) {
+ dbg("could not receive opcode: %#0x", code);
+ return -1;
+ }
+
+ ret = usbip_refresh_device_list(driver);
+ if (ret < 0) {
+ dbg("could not refresh device list: %d", ret);
+ return -1;
+ }
+
+ info("received request: %#0x(%d)", code, connfd);
+ switch (code) {
+ case OP_REQ_DEVLIST:
+ ret = recv_request_devlist(connfd);
+ break;
+ case OP_REQ_IMPORT:
+ ret = recv_request_import(connfd);
+ break;
+ case OP_REQ_DEVINFO:
+ case OP_REQ_CRYPKEY:
+ default:
+ err("received an unknown opcode: %#0x", code);
+ ret = -1;
+ }
+
+ if (ret == 0)
+ info("request %#0x(%d): complete", code, connfd);
+ else
+ info("request %#0x(%d): failed", code, connfd);
+
+ return ret;
+}
+
+#ifdef HAVE_LIBWRAP
+static int tcpd_auth(int connfd)
+{
+ struct request_info request;
+ int rc;
+
+ request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
+ fromhost(&request);
+ rc = hosts_access(&request);
+ if (rc == 0)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static int do_accept(int listenfd)
+{
+ int connfd;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ int rc;
+
+ memset(&ss, 0, sizeof(ss));
+
+ connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
+ if (connfd < 0) {
+ err("failed to accept connection");
+ return -1;
+ }
+
+ rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
+ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc)
+ err("getnameinfo: %s", gai_strerror(rc));
+
+#ifdef HAVE_LIBWRAP
+ rc = tcpd_auth(connfd);
+ if (rc < 0) {
+ info("denied access from %s", host);
+ close(connfd);
+ return -1;
+ }
+#endif
+ info("connection from %s:%s", host, port);
+
+ return connfd;
+}
+
+int process_request(int listenfd)
+{
+ pid_t childpid;
+ int connfd;
+
+ connfd = do_accept(listenfd);
+ if (connfd < 0)
+ return -1;
+ childpid = fork();
+ if (childpid == 0) {
+ close(listenfd);
+ recv_pdu(connfd);
+ exit(0);
+ }
+ close(connfd);
+ return 0;
+}
+
+static void addrinfo_to_text(struct addrinfo *ai, char buf[],
+ const size_t buf_size)
+{
+ char hbuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+ int rc;
+
+ buf[0] = '\0';
+
+ rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc)
+ err("getnameinfo: %s", gai_strerror(rc));
+
+ snprintf(buf, buf_size, "%s:%s", hbuf, sbuf);
+}
+
+static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[],
+ int maxsockfd)
+{
+ struct addrinfo *ai;
+ int ret, nsockfd = 0;
+ const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2;
+ char ai_buf[ai_buf_size];
+
+ for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) {
+ int sock;
+
+ addrinfo_to_text(ai, ai_buf, ai_buf_size);
+ dbg("opening %s", ai_buf);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ err("socket: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ continue;
+ }
+
+ usbip_net_set_reuseaddr(sock);
+ usbip_net_set_nodelay(sock);
+ /* We use separate sockets for IPv4 and IPv6
+ * (see do_standalone_mode()) */
+ usbip_net_set_v6only(sock);
+
+ ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
+ if (ret < 0) {
+ err("bind: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ ret = listen(sock, SOMAXCONN);
+ if (ret < 0) {
+ err("listen: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ info("listening on %s", ai_buf);
+ sockfdlist[nsockfd++] = sock;
+ }
+
+ return nsockfd;
+}
+
+static struct addrinfo *do_getaddrinfo(char *host, int ai_family)
+{
+ struct addrinfo hints, *ai_head;
+ int rc;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head);
+ if (rc) {
+ err("failed to get a network address %s: %s", usbip_port_string,
+ gai_strerror(rc));
+ return NULL;
+ }
+
+ return ai_head;
+}
+
+static void signal_handler(int i)
+{
+ dbg("received '%s' signal", strsignal(i));
+}
+
+static void set_signal(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &act, NULL);
+}
+
+static const char *pid_file;
+
+static void write_pid_file(void)
+{
+ if (pid_file) {
+ dbg("creating pid file %s", pid_file);
+ FILE *fp;
+
+ fp = fopen(pid_file, "w");
+ if (!fp) {
+ err("pid_file: %s: %d (%s)",
+ pid_file, errno, strerror(errno));
+ return;
+ }
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+static void remove_pid_file(void)
+{
+ if (pid_file) {
+ dbg("removing pid file %s", pid_file);
+ unlink(pid_file);
+ }
+}
+
+static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
+{
+ struct addrinfo *ai_head;
+ int sockfdlist[MAXSOCKFD];
+ int nsockfd, family;
+ int i, terminate;
+ struct pollfd *fds;
+ struct timespec timeout;
+ sigset_t sigmask;
+
+ if (usbip_driver_open(driver))
+ return -1;
+
+ if (daemonize) {
+ if (daemon(0, 0) < 0) {
+ err("daemonizing failed: %s", strerror(errno));
+ usbip_driver_close(driver);
+ return -1;
+ }
+ umask(0);
+ usbip_use_syslog = 1;
+ }
+ set_signal();
+ write_pid_file();
+
+ info("starting " PROGNAME " (%s)", usbip_version_string);
+
+ /*
+ * To suppress warnings on systems with bindv6only disabled
+ * (default), we use separate sockets for IPv6 and IPv4 and set
+ * IPV6_V6ONLY on the IPv6 sockets.
+ */
+ if (ipv4 && ipv6)
+ family = AF_UNSPEC;
+ else if (ipv4)
+ family = AF_INET;
+ else
+ family = AF_INET6;
+
+ ai_head = do_getaddrinfo(NULL, family);
+ if (!ai_head) {
+ usbip_driver_close(driver);
+ return -1;
+ }
+ nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
+ sizeof(sockfdlist) / sizeof(*sockfdlist));
+ freeaddrinfo(ai_head);
+ if (nsockfd <= 0) {
+ err("failed to open a listening socket");
+ usbip_driver_close(driver);
+ return -1;
+ }
+
+ dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
+
+ fds = calloc(nsockfd, sizeof(struct pollfd));
+ for (i = 0; i < nsockfd; i++) {
+ fds[i].fd = sockfdlist[i];
+ fds[i].events = POLLIN;
+ }
+ timeout.tv_sec = MAIN_LOOP_TIMEOUT;
+ timeout.tv_nsec = 0;
+
+ sigfillset(&sigmask);
+ sigdelset(&sigmask, SIGTERM);
+ sigdelset(&sigmask, SIGINT);
+
+ terminate = 0;
+ while (!terminate) {
+ int r;
+
+ r = ppoll(fds, nsockfd, &timeout, &sigmask);
+ if (r < 0) {
+ dbg("%s", strerror(errno));
+ terminate = 1;
+ } else if (r) {
+ for (i = 0; i < nsockfd; i++) {
+ if (fds[i].revents & POLLIN) {
+ dbg("read event on fd[%d]=%d",
+ i, sockfdlist[i]);
+ process_request(sockfdlist[i]);
+ }
+ }
+ } else {
+ dbg("heartbeat timeout on ppoll()");
+ }
+ }
+
+ info("shutting down " PROGNAME);
+ free(fds);
+ usbip_driver_close(driver);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "ipv4", no_argument, NULL, '4' },
+ { "ipv6", no_argument, NULL, '6' },
+ { "daemon", no_argument, NULL, 'D' },
+ { "daemon", no_argument, NULL, 'D' },
+ { "debug", no_argument, NULL, 'd' },
+ { "device", no_argument, NULL, 'e' },
+ { "pid", optional_argument, NULL, 'P' },
+ { "tcp-port", required_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ enum {
+ cmd_standalone_mode = 1,
+ cmd_help,
+ cmd_version
+ } cmd;
+
+ int daemonize = 0;
+ int ipv4 = 0, ipv6 = 0;
+ int opt, rc = -1;
+
+ pid_file = NULL;
+
+ usbip_use_stderr = 1;
+ usbip_use_syslog = 0;
+
+ if (geteuid() != 0)
+ err("not running as root?");
+
+ cmd = cmd_standalone_mode;
+ driver = &host_driver;
+ for (;;) {
+ opt = getopt_long(argc, argv, "46DdeP::t:hv", longopts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case '4':
+ ipv4 = 1;
+ break;
+ case '6':
+ ipv6 = 1;
+ break;
+ case 'D':
+ daemonize = 1;
+ break;
+ case 'd':
+ usbip_use_debug = 1;
+ break;
+ case 'h':
+ cmd = cmd_help;
+ break;
+ case 'P':
+ pid_file = optarg ? optarg : DEFAULT_PID_FILE;
+ break;
+ case 't':
+ usbip_setup_port_number(optarg);
+ break;
+ case 'v':
+ cmd = cmd_version;
+ break;
+ case 'e':
+ driver = &device_driver;
+ break;
+ case '?':
+ usbipd_help();
+ default:
+ goto err_out;
+ }
+ }
+
+ if (!ipv4 && !ipv6)
+ ipv4 = ipv6 = 1;
+
+ switch (cmd) {
+ case cmd_standalone_mode:
+ rc = do_standalone_mode(daemonize, ipv4, ipv6);
+ remove_pid_file();
+ break;
+ case cmd_version:
+ printf(PROGNAME " (%s)\n", usbip_version_string);
+ rc = 0;
+ break;
+ case cmd_help:
+ usbipd_help();
+ rc = 0;
+ break;
+ default:
+ usbipd_help();
+ goto err_out;
+ }
+
+err_out:
+ return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/tools/usb/usbip/src/utils.c b/tools/usb/usbip/src/utils.c
new file mode 100644
index 000000000..76a2e1247
--- /dev/null
+++ b/tools/usb/usbip/src/utils.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "sysfs_utils.h"
+
+int modify_match_busid(char *busid, int add)
+{
+ char attr_name[] = "match_busid";
+ char command[SYSFS_BUS_ID_SIZE + 4];
+ char match_busid_attr_path[SYSFS_PATH_MAX];
+ int rc;
+ int cmd_size;
+
+ snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
+ "%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME,
+ SYSFS_BUS_TYPE, SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME,
+ attr_name);
+
+ if (add)
+ cmd_size = snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s",
+ busid);
+ else
+ cmd_size = snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s",
+ busid);
+
+ rc = write_sysfs_attribute(match_busid_attr_path, command,
+ cmd_size);
+ if (rc < 0) {
+ dbg("failed to write match_busid: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tools/usb/usbip/src/utils.h b/tools/usb/usbip/src/utils.h
new file mode 100644
index 000000000..4fc13854f
--- /dev/null
+++ b/tools/usb/usbip/src/utils.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __UTILS_H
+#define __UTILS_H
+
+int modify_match_busid(char *busid, int add);
+
+#endif /* __UTILS_H */
+
diff --git a/tools/usb/usbip/vudc/vudc_server_example.sh b/tools/usb/usbip/vudc/vudc_server_example.sh
new file mode 100755
index 000000000..fed53f51e
--- /dev/null
+++ b/tools/usb/usbip/vudc/vudc_server_example.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+################################################################################
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# For more information, please refer to <https://unlicense.org/>
+################################################################################
+
+################################################################################
+# This is a sample script which shows how to use vUDC with ConfigFS gadgets
+################################################################################
+
+# Stop script on error
+set -e
+
+################################################################################
+# Create your USB gadget
+# You may use bare ConfigFS interface (as below)
+# or libusbgx or gt toool
+# Instead of ConfigFS gadgets you may use any of legacy gadgets.
+################################################################################
+CONFIGFS_MOUNT_POINT="/sys/kernel/config"
+GADGET_NAME="g1"
+ID_VENDOR="0x1d6b"
+ID_PRODUCT="0x0104"
+
+cd ${CONFIGFS_MOUNT_POINT}/usb_gadget
+# Create a new USB gadget
+mkdir ${GADGET_NAME}
+cd ${GADGET_NAME}
+
+# This gadget contains one function - ACM (serial port over USB)
+FUNC_DIR="functions/acm.ser0"
+mkdir ${FUNC_DIR}
+
+# Just one configuration
+mkdir configs/c.1
+ln -s ${FUNC_DIR} configs/c.1
+
+# Set our gadget identity
+echo ${ID_VENDOR} > idVendor
+echo ${ID_PRODUCT} > idProduct
+
+################################################################################
+# Load vudc-module if vudc is not available
+# You may change value of num param to get more than one vUDC instance
+################################################################################
+[[ -d /sys/class/udc/usbip-vudc.0 ]] || modprobe usbip-vudc num=1
+
+################################################################################
+# Bind gadget to our vUDC
+# By default we bind to first one but you may change this if you would like
+# to use more than one instance
+################################################################################
+echo "usbip-vudc.0" > UDC
+
+################################################################################
+# Let's now run our usbip daemon in a USB device mode
+################################################################################
+usbipd --device &
+
+################################################################################
+# Now your USB gadget is available using USB/IP protocol.
+# To prepare your client, you should ensure that usbip-vhci module is inside
+# your kernel. If it's not then you can load it:
+#
+# $ modprobe usbip-vhci
+#
+# To check availability of your gadget you may try to list devices exported
+# on a remote server:
+#
+# $ modprobe usbip-vhci
+# $ usbip list -r $SERVER_IP
+# Exportable USB devices
+# ======================
+# usbipd: info: request 0x8005(6): complete
+# - 127.0.0.1
+# usbip-vudc.0: Linux Foundation : unknown product (1d6b:0104)
+# : /sys/devices/platform/usbip-vudc.0
+# : (Defined at Interface level) (00/00/00)
+#
+# To attach this device to your client you may use:
+#
+# $ usbip attach -r $SERVER_IP -d usbip-vudc.0
+#
+################################################################################