1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 4 * 2005-2007 Takahiro Hirofuchi 5 * Copyright (C) 2015-2016 Samsung Electronics 6 * 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 27 static 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 33 void usbip_attach_usage(void) 34 { 35 printf("usage: %s", usbip_attach_usage_string); 36 } 37 38 #define MAX_BUFF 100 39 static 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 } else 58 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 81 static 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 114 err_driver_close: 115 usbip_vhci_driver_close(); 116 err_out: 117 return -1; 118 } 119 120 static 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 174 static 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 201 int 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 239 err_out: 240 usbip_attach_usage(); 241 out: 242 return ret; 243 } 244