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