@@ -2,7 +2,7 @@ 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])
+AC_DEFINE([USBIP_VERSION], [0x00000112], [binary-coded decimal version number])
CURRENT=0
REVISION=1
@@ -78,6 +78,7 @@ static struct op_common_status_string op_common_status_strings[] = {
{ ST_DEV_ERR, "Device in error state" },
{ ST_NODEV, "Device not found" },
{ ST_ERROR, "Unexpected response" },
+ { ST_POLL_TIMEOUT, "Poll timeout" },
{ 0, NULL}
};
@@ -53,6 +53,7 @@
#define ST_DEV_ERR 0x03
#define ST_NODEV 0x04
#define ST_ERROR 0x05
+#define ST_POLL_TIMEOUT 0x06
extern int usbip_use_syslog;
extern int usbip_use_stderr;
@@ -465,3 +465,19 @@ int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
return 0;
}
+
+int usbip_vhci_get_local_busid_from(int port, char *local_busid)
+{
+ int rc = -1;
+
+ for (int i = 0; i < vhci_driver->nports; ++i) {
+ struct usbip_imported_device *idev = &vhci_driver->idev[i];
+
+ if (idev->port == port && strnlen(idev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+ memcpy(local_busid, idev->udev.busid, SYSFS_BUS_ID_SIZE);
+ rc = 0;
+ break;
+ }
+ }
+ return rc;
+}
@@ -63,5 +63,6 @@ int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
int usbip_vhci_detach_device(uint8_t port);
int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
+int usbip_vhci_get_local_busid_from(int port, char *local_busid);
#endif /* __VHCI_DRIVER_H */
@@ -12,6 +12,7 @@
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
@@ -21,14 +22,23 @@
#include "vhci_driver.h"
#include "usbip_common.h"
+#include "usbip_monitor.h"
#include "usbip_network.h"
#include "usbip.h"
+struct attach_options {
+ char *busid;
+ bool is_persistent;
+};
+
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";
+ " -b, --busid=<busid> Busid of the device on <host>\n"
+ " -d, --device=<devid> Id of the virtual UDC on <host>\n"
+ " -p, --persistent Persistently monitor the given bus and import\n"
+ " USB devices when available on the remote end\n";
+
void usbip_attach_usage(void)
{
@@ -117,7 +127,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev)
return -1;
}
-static int query_import_device(int sockfd, char *busid)
+static int query_import_device(int sockfd, char *busid, bool is_persistent)
{
int rc;
struct op_import_request request;
@@ -127,31 +137,35 @@ static int query_import_device(int sockfd, char *busid)
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);
+ if (is_persistent) {
+ request.poll_timeout_ms = 5000;
+ info("remote device on busid %s: polling", busid);
+ }
+ PACK_OP_IMPORT_REQUEST(1, &request);
- PACK_OP_IMPORT_REQUEST(0, &request);
+ do {
+ /* send a request */
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+ if (rc < 0) {
+ err("send op_common");
+ return -1;
+ }
- rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
- if (rc < 0) {
- err("send op_import_request");
- return -1;
- }
+ 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;
- }
+ /* receive a reply */
+ rc = usbip_net_recv_op_common(sockfd, &code, &status);
+ if (status != ST_POLL_TIMEOUT && rc < 0) {
+ err("Attach Request for %s failed - %s\n",
+ busid, usbip_op_common_status_string(status));
+ return -1;
+ }
+ } while (status == ST_POLL_TIMEOUT);
rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
if (rc < 0) {
@@ -171,7 +185,17 @@ static int query_import_device(int sockfd, char *busid)
return import_device(sockfd, &reply.udev);
}
-static int attach_device(char *host, char *busid)
+static int get_local_busid_from(int port, char *local_busid)
+{
+ int rc = usbip_vhci_driver_open();
+
+ if (rc == 0)
+ rc = usbip_vhci_get_local_busid_from(port, local_busid);
+ usbip_vhci_driver_close();
+ return rc;
+}
+
+static int attach_device(char *host, struct attach_options opt)
{
int sockfd;
int rc;
@@ -183,19 +207,53 @@ static int attach_device(char *host, char *busid)
return -1;
}
- rhport = query_import_device(sockfd, busid);
+ rhport = query_import_device(sockfd, opt.busid, opt.is_persistent);
if (rhport < 0)
return -1;
close(sockfd);
- rc = record_connection(host, usbip_port_string, busid, rhport);
+ rc = record_connection(host, usbip_port_string, opt.busid, rhport);
if (rc < 0) {
- err("record connection");
+ err("Fail occurred when storing imported usbip device");
return -1;
}
+ info("remote device on busid %s: attach complete", opt.busid);
+ return rhport;
+}
- return 0;
+static void monitor_disconnect(usbip_monitor_t *monitor, char *busid, int rhport)
+{
+ // To monitor unbind we must first ensure to be at a bound state. To
+ // monitor bound state a local busid is needed, which is unknown at this
+ // moment. Local busid is not available until it's already bound to the usbip
+ // driver. Thus monitor bind events for any usb device until the busid is
+ // available for the port.
+ char local_busid[SYSFS_BUS_ID_SIZE] = {};
+
+ while (get_local_busid_from(rhport, local_busid))
+ usbip_monitor_await_usb_bind(monitor, USBIP_USB_DRV_NAME);
+ info("remote device on busid %s: monitor disconnect", busid);
+ usbip_monitor_set_busid(monitor, local_busid);
+ usbip_monitor_await_usb_unbind(monitor);
+ usbip_monitor_set_busid(monitor, NULL);
+}
+
+static int attach_device_persistently(char *host, struct attach_options opt)
+{
+ int rc = 0;
+ usbip_monitor_t *monitor = usbip_monitor_new();
+
+ while (rc == 0) {
+ int rhport = attach_device(host, opt);
+
+ if (rhport < 0)
+ rc = -1;
+ else
+ monitor_disconnect(monitor, opt.busid, rhport);
+ }
+ usbip_monitor_delete(monitor);
+ return rc;
}
int usbip_attach(int argc, char *argv[])
@@ -204,15 +262,16 @@ int usbip_attach(int argc, char *argv[])
{ "remote", required_argument, NULL, 'r' },
{ "busid", required_argument, NULL, 'b' },
{ "device", required_argument, NULL, 'd' },
+ { "persistent", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};
char *host = NULL;
- char *busid = NULL;
+ struct attach_options options = {};
int opt;
int ret = -1;
for (;;) {
- opt = getopt_long(argc, argv, "d:r:b:", opts, NULL);
+ opt = getopt_long(argc, argv, "d:r:b:p", opts, NULL);
if (opt == -1)
break;
@@ -223,17 +282,24 @@ int usbip_attach(int argc, char *argv[])
break;
case 'd':
case 'b':
- busid = optarg;
+ options.busid = optarg;
+ break;
+ case 'p':
+ options.is_persistent = true;
break;
default:
goto err_out;
}
}
- if (!host || !busid)
+ if (!host || !options.busid)
goto err_out;
- ret = attach_device(host, busid);
+ if (options.is_persistent)
+ ret = attach_device_persistently(host, options);
+ else
+ ret = attach_device(host, options);
+
goto out;
err_out:
@@ -61,6 +61,7 @@ struct op_devinfo_reply {
struct op_import_request {
char busid[SYSFS_BUS_ID_SIZE];
+ uint32_t poll_timeout_ms;
} __attribute__((packed));
struct op_import_reply {
@@ -69,6 +70,7 @@ struct op_import_reply {
} __attribute__((packed));
#define PACK_OP_IMPORT_REQUEST(pack, request) do {\
+ (request)->poll_timeout_ms = usbip_net_pack_uint32_t(pack, (request)->poll_timeout_ms);\
} while (0)
#define PACK_OP_IMPORT_REPLY(pack, reply) do {\
@@ -22,6 +22,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <stdbool.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
@@ -35,6 +36,7 @@
#include "usbip_host_common.h"
#include "usbip_device_driver.h"
#include "usbip_common.h"
+#include "usbip_monitor.h"
#include "usbip_network.h"
#include "list.h"
@@ -88,13 +90,78 @@ static void usbipd_help(void)
printf("%s\n", usbipd_help_string);
}
+static struct usbip_exported_device *get_exported_device(const char *busid)
+{
+ struct usbip_exported_device *exported_dev = NULL;
+ struct usbip_exported_device *edev = NULL;
+ struct list_head *i;
+
+ list_for_each(i, &driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ if (!strncmp(busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+ exported_dev = edev;
+ break;
+ }
+ }
+ return exported_dev;
+}
+
+static bool await_requested_device(usbip_monitor_t *monitor,
+ struct op_import_request *req)
+{
+ usbip_monitor_set_busid(monitor, req->busid);
+ usbip_monitor_set_timeout(monitor, req->poll_timeout_ms);
+ return usbip_monitor_await_usb_bind(monitor, "usbip-host");
+}
+
+static int recv_subsequent_poll_request(int sockfd, struct op_import_request *req)
+{
+ uint16_t code = OP_UNSPEC;
+ int status;
+ int rc = 0;
+
+ rc = usbip_net_recv_op_common(sockfd, &code, &status);
+ if (rc < 0) {
+ dbg("could not receive opcode: %#0x", code);
+ return -1;
+ }
+ if (code != OP_REQ_IMPORT) {
+ dbg("Only subsequent OP_REQ_IMPORT allowed when polling");
+ return -1;
+ }
+ rc = usbip_net_recv(sockfd, req, sizeof(struct op_import_request));
+ if (rc < 0) {
+ dbg("Failed to receive incoming subsequent OP_REQ_IMPORT request");
+ return -1;
+ }
+ PACK_OP_IMPORT_REQUEST(0, req);
+ return rc;
+}
+
+static int monitor_requested_busid(int sockfd, struct op_import_request *req)
+{
+ int rc = 0;
+ usbip_monitor_t *monitor = usbip_monitor_new();
+
+ while (!await_requested_device(monitor, req)) {
+ int status = ST_POLL_TIMEOUT;
+
+ 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);
+ break;
+ }
+ rc = recv_subsequent_poll_request(sockfd, req);
+ }
+ usbip_monitor_delete(monitor);
+ return rc;
+}
+
static int recv_request_import(int sockfd)
{
struct op_import_request req;
- struct usbip_exported_device *edev;
+ struct usbip_exported_device *edev = NULL;
struct usbip_usb_device pdu_udev;
- struct list_head *i;
- int found = 0;
int status = ST_OK;
int rc;
@@ -107,16 +174,22 @@ static int recv_request_import(int sockfd)
}
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;
+ edev = get_exported_device(req.busid);
+
+ if (!edev && req.poll_timeout_ms) {
+ info("Client polling for devices on busid: %s", req.busid);
+ rc = monitor_requested_busid(sockfd, &req);
+ if (rc < 0) {
+ dbg("Fail occurred when monitoring usb to become exported on busid: %s",
+ req.busid);
+ return -1;
}
+ usbip_refresh_device_list(driver);
+ edev = get_exported_device(req.busid);
}
- if (found) {
+ if (edev) {
+ info("found requested device: %s", req.busid);
/* should set TCP_NODELAY for usbip */
usbip_net_set_nodelay(sockfd);
This patch extends the command "usbip attach" with flag "-p|--persistent". When this flag is used, devices on the given remote busid is imported when available, instead of returning an error if the device is not exported. When the usb device is unplugged, it will start monitor the given remote bus again. Increment protocol version to 1.1.2 (0x112) since this patch affects both ends: usbipd (server) and command "usbip attach" (client). Signed-off-by: Lars Gunnarsson <gunnarsson.lars@gmail.com> --- v2: Change title, fix style warnings, improve feature description, refactor cmdline flag usage. v3: Change title and description. v4: Fix review comments. tools/usb/usbip/configure.ac | 2 +- tools/usb/usbip/libsrc/usbip_common.c | 1 + tools/usb/usbip/libsrc/usbip_common.h | 1 + tools/usb/usbip/libsrc/vhci_driver.c | 16 +++ tools/usb/usbip/libsrc/vhci_driver.h | 1 + tools/usb/usbip/src/usbip_attach.c | 134 +++++++++++++++++++------- tools/usb/usbip/src/usbip_network.h | 2 + tools/usb/usbip/src/usbipd.c | 93 ++++++++++++++++-- 8 files changed, 205 insertions(+), 45 deletions(-) -- 2.25.1