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