Extend command "usbip attach" with: * flag "-c" to wait for busid to become exported on remote end, and then attach, instead of returning error. * flag "-d", to wait for busid to disconnect before exit. Today manual steps is required to re-import usb device when re-connected. With above flags enabled, it's possible to permanently "import" usb devices with help of example systemd or other process supervisors, or with a simple while-loop in bash. With patch [1/3] and [2/3] it is possible to fully support usb devices to be reconnected over both ends. Note: * Reclaimed flag "-d" which was superflous and no different to "-b". * This patch affects both ends: usbipd (server) and command "usbip attach" (client). Usbip must preferably have both ends of the new versions. Signed-off-by: Lars Gunnarsson <gunnarsson.lars@xxxxxxxxx> --- tools/usb/usbip/libsrc/vhci_driver.c | 16 ++++++ tools/usb/usbip/libsrc/vhci_driver.h | 1 + tools/usb/usbip/src/usbip_attach.c | 80 ++++++++++++++++++++++------ tools/usb/usbip/src/usbip_network.h | 1 + tools/usb/usbip/src/usbipd.c | 42 +++++++++++---- 5 files changed, 114 insertions(+), 26 deletions(-) diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c index 8159fd98680b..4fc75e8bad66 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.c +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -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; +} diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h index 6c9aca216705..4aecd4013cbe 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.h +++ b/tools/usb/usbip/libsrc/vhci_driver.h @@ -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 */ diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c index b4aeb9f1f493..13388bf9800f 100644 --- a/tools/usb/usbip/src/usbip_attach.c +++ b/tools/usb/usbip/src/usbip_attach.c @@ -21,14 +21,23 @@ #include "vhci_driver.h" #include "usbip_common.h" +#include "usbip_monitor.h" #include "usbip_network.h" #include "usbip.h" +typedef struct { + char *busid; + char monitor_connect; + char monitor_disconnect; +} attach_options; + 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" + " or id of the virtual UDC on <host>\n" + " -c, --monitor-connect Wait until bus is available before binding\n" + " -d, --monitor-disconnect Wait until bus is disconnected before exit\n"; void usbip_attach_usage(void) { @@ -117,7 +126,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, char monitor_connect) { int rc; struct op_import_request request; @@ -135,9 +144,14 @@ static int query_import_device(int sockfd, char *busid) return -1; } + if (monitor_connect) { + info("remote device on busid %s: awaiting connect", busid); + request.monitor_connect = 1; + } + strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1); - PACK_OP_IMPORT_REQUEST(0, &request); + PACK_OP_IMPORT_REQUEST(1, &request); rc = usbip_net_send(sockfd, (void *) &request, sizeof(request)); if (rc < 0) { @@ -171,11 +185,27 @@ 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, attach_options opt) { int sockfd; int rc; int rhport; + usbip_monitor_t *monitor = NULL; + + if (opt.monitor_disconnect) { + monitor = usbip_monitor_new(); + } sockfd = usbip_net_tcp_connect(host, usbip_port_string); if (sockfd < 0) { @@ -183,19 +213,33 @@ static int attach_device(char *host, char *busid) return -1; } - rhport = query_import_device(sockfd, busid); + rhport = query_import_device(sockfd, opt.busid, opt.monitor_connect); 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"); return -1; } - return 0; + if (opt.monitor_disconnect) { + // local busid is needed to monitor events, which is unknown at this moment. + // Thus monitor bind events for any usb device until the busid is + // available for current 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: awaiting disconnect", opt.busid); + usbip_monitor_set_busid(monitor, local_busid); + usbip_monitor_await_usb_unbind(monitor); + usbip_monitor_delete(monitor); + } + return rc; } int usbip_attach(int argc, char *argv[]) @@ -203,16 +247,17 @@ 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' }, + { "monitor-connect", no_argument, NULL, 'c' }, + { "monitor-disconnect", no_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; char *host = NULL; - char *busid = NULL; + attach_options options = {}; int opt; int ret = -1; for (;;) { - opt = getopt_long(argc, argv, "d:r:b:", opts, NULL); + opt = getopt_long(argc, argv, "r:b:cd", opts, NULL); if (opt == -1) break; @@ -221,19 +266,24 @@ int usbip_attach(int argc, char *argv[]) case 'r': host = optarg; break; - case 'd': case 'b': - busid = optarg; + options.busid = optarg; + break; + case 'c': + options.monitor_connect = 1; + break; + case 'd': + options.monitor_disconnect = 1; break; default: goto err_out; } } - if (!host || !busid) + if (!host || !options.busid) goto err_out; - ret = attach_device(host, busid); + ret = attach_device(host, options); goto out; err_out: diff --git a/tools/usb/usbip/src/usbip_network.h b/tools/usb/usbip/src/usbip_network.h index 83b4c5344f72..b9b80d24e68d 100644 --- a/tools/usb/usbip/src/usbip_network.h +++ b/tools/usb/usbip/src/usbip_network.h @@ -61,6 +61,7 @@ struct op_devinfo_reply { struct op_import_request { char busid[SYSFS_BUS_ID_SIZE]; + char monitor_connect; } __attribute__((packed)); struct op_import_reply { diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c index 48398a78e88a..612758922fc1 100644 --- a/tools/usb/usbip/src/usbipd.c +++ b/tools/usb/usbip/src/usbipd.c @@ -35,6 +35,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 +89,27 @@ 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 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 +122,21 @@ 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.monitor_connect) { + info("Client waiting on busid: %s to be exported", req.busid); + usbip_monitor_t *monitor = usbip_monitor_new(); + + usbip_monitor_set_busid(monitor, req.busid); + usbip_monitor_await_usb_bind(monitor, "usbip-host"); + usbip_monitor_delete(monitor); + 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); -- 2.25.1