244 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>4 * 2005-2007 Takahiro Hirofuchi5 * Copyright (C) 2015-2016 Samsung Electronics6 * Igor Kotrasinski <i.kotrasinsk@samsung.com>7 * Krzysztof Opasiak <k.opasiak@samsung.com>8 */9 10#include <sys/stat.h>11 12#include <limits.h>13#include <stdint.h>14#include <stdio.h>15#include <string.h>16 17#include <fcntl.h>18#include <getopt.h>19#include <unistd.h>20#include <errno.h>21 22#include "vhci_driver.h"23#include "usbip_common.h"24#include "usbip_network.h"25#include "usbip.h"26 27static const char usbip_attach_usage_string[] =28 "usbip attach <args>\n"29 " -r, --remote=<host> The machine with exported USB devices\n"30 " -b, --busid=<busid> Busid of the device on <host>\n"31 " -d, --device=<devid> Id of the virtual UDC on <host>\n";32 33void usbip_attach_usage(void)34{35 printf("usage: %s", usbip_attach_usage_string);36}37 38#define MAX_BUFF 10039static int record_connection(char *host, char *port, char *busid, int rhport)40{41 int fd;42 char path[PATH_MAX+1];43 char buff[MAX_BUFF+1];44 int ret;45 46 ret = mkdir(VHCI_STATE_PATH, 0700);47 if (ret < 0) {48 /* if VHCI_STATE_PATH exists, then it better be a directory */49 if (errno == EEXIST) {50 struct stat s;51 52 ret = stat(VHCI_STATE_PATH, &s);53 if (ret < 0)54 return -1;55 if (!(s.st_mode & S_IFDIR))56 return -1;57 } else58 return -1;59 }60 61 snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);62 63 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);64 if (fd < 0)65 return -1;66 67 snprintf(buff, MAX_BUFF, "%s %s %s\n",68 host, port, busid);69 70 ret = write(fd, buff, strlen(buff));71 if (ret != (ssize_t) strlen(buff)) {72 close(fd);73 return -1;74 }75 76 close(fd);77 78 return 0;79}80 81static int import_device(int sockfd, struct usbip_usb_device *udev)82{83 int rc;84 int port;85 uint32_t speed = udev->speed;86 87 rc = usbip_vhci_driver_open();88 if (rc < 0) {89 err("open vhci_driver (is vhci_hcd loaded?)");90 goto err_out;91 }92 93 do {94 port = usbip_vhci_get_free_port(speed);95 if (port < 0) {96 err("no free port");97 goto err_driver_close;98 }99 100 dbg("got free port %d", port);101 102 rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,103 udev->devnum, udev->speed);104 if (rc < 0 && errno != EBUSY) {105 err("import device");106 goto err_driver_close;107 }108 } while (rc < 0);109 110 usbip_vhci_driver_close();111 112 return port;113 114err_driver_close:115 usbip_vhci_driver_close();116err_out:117 return -1;118}119 120static int query_import_device(int sockfd, char *busid)121{122 int rc;123 struct op_import_request request;124 struct op_import_reply reply;125 uint16_t code = OP_REP_IMPORT;126 int status;127 128 memset(&request, 0, sizeof(request));129 memset(&reply, 0, sizeof(reply));130 131 /* send a request */132 rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);133 if (rc < 0) {134 err("send op_common");135 return -1;136 }137 138 strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);139 140 PACK_OP_IMPORT_REQUEST(0, &request);141 142 rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));143 if (rc < 0) {144 err("send op_import_request");145 return -1;146 }147 148 /* receive a reply */149 rc = usbip_net_recv_op_common(sockfd, &code, &status);150 if (rc < 0) {151 err("Attach Request for %s failed - %s\n",152 busid, usbip_op_common_status_string(status));153 return -1;154 }155 156 rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));157 if (rc < 0) {158 err("recv op_import_reply");159 return -1;160 }161 162 PACK_OP_IMPORT_REPLY(0, &reply);163 164 /* check the reply */165 if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {166 err("recv different busid %s", reply.udev.busid);167 return -1;168 }169 170 /* import a device */171 return import_device(sockfd, &reply.udev);172}173 174static int attach_device(char *host, char *busid)175{176 int sockfd;177 int rc;178 int rhport;179 180 sockfd = usbip_net_tcp_connect(host, usbip_port_string);181 if (sockfd < 0) {182 err("tcp connect");183 return -1;184 }185 186 rhport = query_import_device(sockfd, busid);187 if (rhport < 0)188 return -1;189 190 close(sockfd);191 192 rc = record_connection(host, usbip_port_string, busid, rhport);193 if (rc < 0) {194 err("record connection");195 return -1;196 }197 198 return 0;199}200 201int usbip_attach(int argc, char *argv[])202{203 static const struct option opts[] = {204 { "remote", required_argument, NULL, 'r' },205 { "busid", required_argument, NULL, 'b' },206 { "device", required_argument, NULL, 'd' },207 { NULL, 0, NULL, 0 }208 };209 char *host = NULL;210 char *busid = NULL;211 int opt;212 int ret = -1;213 214 for (;;) {215 opt = getopt_long(argc, argv, "d:r:b:", opts, NULL);216 217 if (opt == -1)218 break;219 220 switch (opt) {221 case 'r':222 host = optarg;223 break;224 case 'd':225 case 'b':226 busid = optarg;227 break;228 default:229 goto err_out;230 }231 }232 233 if (!host || !busid)234 goto err_out;235 236 ret = attach_device(host, busid);237 goto out;238 239err_out:240 usbip_attach_usage();241out:242 return ret;243}244