// 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; }