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 return -1; 103 } 104 105 port = usbip_vhci_get_free_port(speed); 106 if (port < 0) { 107 err("no free port"); 108 usbip_vhci_driver_close(); 109 return -1; 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) { 117 err("import device"); 118 usbip_vhci_driver_close(); 119 return -1; 120 } 121 122 usbip_vhci_driver_close(); 123 124 return port; 125 } 126 127 static int query_import_device(int sockfd, char *busid) 128 { 129 int rc; 130 struct op_import_request request; 131 struct op_import_reply reply; 132 uint16_t code = OP_REP_IMPORT; 133 134 memset(&request, 0, sizeof(request)); 135 memset(&reply, 0, sizeof(reply)); 136 137 /* send a request */ 138 rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0); 139 if (rc < 0) { 140 err("send op_common"); 141 return -1; 142 } 143 144 strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1); 145 146 PACK_OP_IMPORT_REQUEST(0, &request); 147 148 rc = usbip_net_send(sockfd, (void *) &request, sizeof(request)); 149 if (rc < 0) { 150 err("send op_import_request"); 151 return -1; 152 } 153 154 /* receive a reply */ 155 rc = usbip_net_recv_op_common(sockfd, &code); 156 if (rc < 0) { 157 err("recv op_common"); 158 return -1; 159 } 160 161 rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply)); 162 if (rc < 0) { 163 err("recv op_import_reply"); 164 return -1; 165 } 166 167 PACK_OP_IMPORT_REPLY(0, &reply); 168 169 /* check the reply */ 170 if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) { 171 err("recv different busid %s", reply.udev.busid); 172 return -1; 173 } 174 175 /* import a device */ 176 return import_device(sockfd, &reply.udev); 177 } 178 179 static int attach_device(char *host, char *busid) 180 { 181 int sockfd; 182 int rc; 183 int rhport; 184 185 sockfd = usbip_net_tcp_connect(host, usbip_port_string); 186 if (sockfd < 0) { 187 err("tcp connect"); 188 return -1; 189 } 190 191 rhport = query_import_device(sockfd, busid); 192 if (rhport < 0) { 193 err("query"); 194 return -1; 195 } 196 197 close(sockfd); 198 199 rc = record_connection(host, usbip_port_string, busid, rhport); 200 if (rc < 0) { 201 err("record connection"); 202 return -1; 203 } 204 205 return 0; 206 } 207 208 int usbip_attach(int argc, char *argv[]) 209 { 210 static const struct option opts[] = { 211 { "remote", required_argument, NULL, 'r' }, 212 { "busid", required_argument, NULL, 'b' }, 213 { "device", required_argument, NULL, 'd' }, 214 { NULL, 0, NULL, 0 } 215 }; 216 char *host = NULL; 217 char *busid = NULL; 218 int opt; 219 int ret = -1; 220 221 for (;;) { 222 opt = getopt_long(argc, argv, "d:r:b:", opts, NULL); 223 224 if (opt == -1) 225 break; 226 227 switch (opt) { 228 case 'r': 229 host = optarg; 230 break; 231 case 'd': 232 case 'b': 233 busid = optarg; 234 break; 235 default: 236 goto err_out; 237 } 238 } 239 240 if (!host || !busid) 241 goto err_out; 242 243 ret = attach_device(host, busid); 244 goto out; 245 246 err_out: 247 usbip_attach_usage(); 248 out: 249 return ret; 250 } 251