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