diff --git a/Documentation/usb/usbip_protocol.rst b/Documentation/usb/usbip_protocol.rst
index 0b8541fda4d8..2ed7d97278e8 100644
--- a/Documentation/usb/usbip_protocol.rst
+++ b/Documentation/usb/usbip_protocol.rst
@@ -9,6 +9,11 @@ The USB/IP protocol follows a server/client architecture. The server exports the
USB devices and the clients import them. The device driver for the exported
USB device runs on the client machine.
+Initially the server may choose to export any of its available USB devices,
+based on the busid. The device will remain exported until it's unplugged or
+unbound from the usbip driver. It is also possible to persistently export
+devices on a given bus by monitor when they are plugged in.
+
The client may ask for the list of the exported USB devices. To get the list the
client opens a TCP/IP connection to the server, and sends an OP_REQ_DEVLIST
packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent
diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c
index f1cf9225a69c..fd4ccbfce6c6 100644
--- a/tools/usb/usbip/src/usbip_bind.c
+++ b/tools/usb/usbip/src/usbip_bind.c
@@ -8,12 +8,14 @@
#include <errno.h>
#include <stdio.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "usbip_common.h"
+#include "usbip_monitor.h"
#include "utils.h"
#include "usbip.h"
#include "sysfs_utils.h"
@@ -24,10 +26,17 @@ enum unbind_status {
UNBIND_ST_FAILED
};
+struct bind_options {
+ char *busid;
+ bool is_persistent;
+};
+
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";
+ " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device\n"
+ " on <busid>\n"
+ " -p, --persistent Persistently monitor the given bus and\n"
+ " export USB devices plugged in\n";
void usbip_bind_usage(void)
{@@ -127,29 +136,26 @@ static int unbind_other(char *busid)
return status;
}
-static int bind_device(char *busid)
+static const char *get_device_devpath(char *busid)
{
- int rc;
- struct udev *udev;
- struct udev_device *dev;
- const char *devpath;
+ struct udev *udev = udev_new();
+ const char *devpath = NULL;
+ struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
- /* 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);
+ if (dev)
+ devpath = udev_device_get_devpath(dev);
udev_unref(udev);
+ return devpath;
+}
- /* 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;
- }
+static bool is_usb_connected(char *busid)
+{
+ return get_device_devpath(busid) != NULL;
+}
+
+static int bind_available_device(char *busid)
+{
+ int rc;
rc = unbind_other(busid);
if (rc == UNBIND_ST_FAILED) {@@ -174,36 +180,94 @@ static int bind_device(char *busid)
return -1;
}
- info("bind device on busid %s: complete", busid);
+ info("device on busid %s: bind complete", busid);
return 0;
}
+static int bind_device(char *busid)
+{
+ const char *devpath = get_device_devpath(busid);
+
+ /* Check whether the device with this bus ID exists. */
+ if (!devpath) {
+ err("device with the specified bus ID does not exist");
+ return -1;
+ }
+
+ /* 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;
+ }
+
+ return bind_available_device(busid);
+}
+
+static int bind_device_persistently(char *busid)
+{
+ int rc = 0;
+ bool already_connected = is_usb_connected(busid);
+ usbip_monitor_t *monitor = monitor = usbip_monitor_new();
+
+ usbip_monitor_set_busid(monitor, busid);
+ while (rc == 0) {
+ if (!already_connected) {
+ info("device on busid %s: monitor connect", busid);
+ usbip_monitor_await_usb_bind(monitor, USBIP_USB_DRV_NAME);
+ }
+ rc = bind_available_device(busid);
+ if (rc == 0) {
+ info("device on busid %s: monitor disconnect", busid);
+ usbip_monitor_await_usb_bind(monitor, USBIP_HOST_DRV_NAME);
+ usbip_monitor_await_usb_unbind(monitor);
+ }
+ already_connected = false;
+ }
+ usbip_monitor_delete(monitor);
+ return rc;
+}
+
int usbip_bind(int argc, char *argv[])
{
static const struct option opts[] = {
{ "busid", required_argument, NULL, 'b' },
+ { "persistent", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};
+ struct bind_options options = {};
int opt;
int ret = -1;
for (;;) {
- opt = getopt_long(argc, argv, "b:", opts, NULL);
+ opt = getopt_long(argc, argv, "b:p", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'b':
- ret = bind_device(optarg);
- goto out;
+ options.busid = optarg;
+ break;
+ case 'p':
+ options.is_persistent = true;
+ break;
default:
goto err_out;
}
}
+ if (!options.busid)
+ goto err_out;
+
+ if (options.is_persistent)
+ ret = bind_device_persistently(options.busid);
+ else
+ ret = bind_device(options.busid);
+ goto out;
+
err_out:
usbip_bind_usage();
out:--
2.25.1