summaryrefslogtreecommitdiffstats
path: root/tools/usb/usbip/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
commit5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch)
treea94efe259b9009378be6d90eb30d2b019d95c194 /tools/usb/usbip/src
parentInitial commit. (diff)
downloadlinux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz
linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-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.c124
-rw-r--r--tools/usb/usbip/src/usbip_list.c372
-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
14 files changed, 2590 insertions, 0 deletions
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..b4aeb9f1f
--- /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");
+ 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..aec993159
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_detach.c
@@ -0,0 +1,124 @@
+// 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");
+ 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) {
+ 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..8625b0f51
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_list.c
@@ -0,0 +1,372 @@
+// 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";
+
+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..4d14387df
--- /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");
+ 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..48398a78e
--- /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 seperate 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 seperate 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 */
+