18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 48c2ecf20Sopenharmony_ci * 2005-2007 Takahiro Hirofuchi 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics 68c2ecf20Sopenharmony_ci * Igor Kotrasinski <i.kotrasinsk@samsung.com> 78c2ecf20Sopenharmony_ci * Krzysztof Opasiak <k.opasiak@samsung.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <sys/stat.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <limits.h> 138c2ecf20Sopenharmony_ci#include <stdint.h> 148c2ecf20Sopenharmony_ci#include <stdio.h> 158c2ecf20Sopenharmony_ci#include <string.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <fcntl.h> 188c2ecf20Sopenharmony_ci#include <getopt.h> 198c2ecf20Sopenharmony_ci#include <unistd.h> 208c2ecf20Sopenharmony_ci#include <errno.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "vhci_driver.h" 238c2ecf20Sopenharmony_ci#include "usbip_common.h" 248c2ecf20Sopenharmony_ci#include "usbip_network.h" 258c2ecf20Sopenharmony_ci#include "usbip.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const char usbip_attach_usage_string[] = 288c2ecf20Sopenharmony_ci "usbip attach <args>\n" 298c2ecf20Sopenharmony_ci " -r, --remote=<host> The machine with exported USB devices\n" 308c2ecf20Sopenharmony_ci " -b, --busid=<busid> Busid of the device on <host>\n" 318c2ecf20Sopenharmony_ci " -d, --device=<devid> Id of the virtual UDC on <host>\n"; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_civoid usbip_attach_usage(void) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci printf("usage: %s", usbip_attach_usage_string); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MAX_BUFF 100 398c2ecf20Sopenharmony_cistatic int record_connection(char *host, char *port, char *busid, int rhport) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int fd; 428c2ecf20Sopenharmony_ci char path[PATH_MAX+1]; 438c2ecf20Sopenharmony_ci char buff[MAX_BUFF+1]; 448c2ecf20Sopenharmony_ci int ret; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci ret = mkdir(VHCI_STATE_PATH, 0700); 478c2ecf20Sopenharmony_ci if (ret < 0) { 488c2ecf20Sopenharmony_ci /* if VHCI_STATE_PATH exists, then it better be a directory */ 498c2ecf20Sopenharmony_ci if (errno == EEXIST) { 508c2ecf20Sopenharmony_ci struct stat s; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci ret = stat(VHCI_STATE_PATH, &s); 538c2ecf20Sopenharmony_ci if (ret < 0) 548c2ecf20Sopenharmony_ci return -1; 558c2ecf20Sopenharmony_ci if (!(s.st_mode & S_IFDIR)) 568c2ecf20Sopenharmony_ci return -1; 578c2ecf20Sopenharmony_ci } else 588c2ecf20Sopenharmony_ci return -1; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); 648c2ecf20Sopenharmony_ci if (fd < 0) 658c2ecf20Sopenharmony_ci return -1; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci snprintf(buff, MAX_BUFF, "%s %s %s\n", 688c2ecf20Sopenharmony_ci host, port, busid); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = write(fd, buff, strlen(buff)); 718c2ecf20Sopenharmony_ci if (ret != (ssize_t) strlen(buff)) { 728c2ecf20Sopenharmony_ci close(fd); 738c2ecf20Sopenharmony_ci return -1; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci close(fd); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int import_device(int sockfd, struct usbip_usb_device *udev) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int rc; 848c2ecf20Sopenharmony_ci int port; 858c2ecf20Sopenharmony_ci uint32_t speed = udev->speed; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci rc = usbip_vhci_driver_open(); 888c2ecf20Sopenharmony_ci if (rc < 0) { 898c2ecf20Sopenharmony_ci err("open vhci_driver"); 908c2ecf20Sopenharmony_ci goto err_out; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci do { 948c2ecf20Sopenharmony_ci port = usbip_vhci_get_free_port(speed); 958c2ecf20Sopenharmony_ci if (port < 0) { 968c2ecf20Sopenharmony_ci err("no free port"); 978c2ecf20Sopenharmony_ci goto err_driver_close; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci dbg("got free port %d", port); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci rc = usbip_vhci_attach_device(port, sockfd, udev->busnum, 1038c2ecf20Sopenharmony_ci udev->devnum, udev->speed); 1048c2ecf20Sopenharmony_ci if (rc < 0 && errno != EBUSY) { 1058c2ecf20Sopenharmony_ci err("import device"); 1068c2ecf20Sopenharmony_ci goto err_driver_close; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } while (rc < 0); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci usbip_vhci_driver_close(); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return port; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cierr_driver_close: 1158c2ecf20Sopenharmony_ci usbip_vhci_driver_close(); 1168c2ecf20Sopenharmony_cierr_out: 1178c2ecf20Sopenharmony_ci return -1; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int query_import_device(int sockfd, char *busid) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int rc; 1238c2ecf20Sopenharmony_ci struct op_import_request request; 1248c2ecf20Sopenharmony_ci struct op_import_reply reply; 1258c2ecf20Sopenharmony_ci uint16_t code = OP_REP_IMPORT; 1268c2ecf20Sopenharmony_ci int status; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 1298c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* send a request */ 1328c2ecf20Sopenharmony_ci rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0); 1338c2ecf20Sopenharmony_ci if (rc < 0) { 1348c2ecf20Sopenharmony_ci err("send op_common"); 1358c2ecf20Sopenharmony_ci return -1; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci PACK_OP_IMPORT_REQUEST(0, &request); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci rc = usbip_net_send(sockfd, (void *) &request, sizeof(request)); 1438c2ecf20Sopenharmony_ci if (rc < 0) { 1448c2ecf20Sopenharmony_ci err("send op_import_request"); 1458c2ecf20Sopenharmony_ci return -1; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* receive a reply */ 1498c2ecf20Sopenharmony_ci rc = usbip_net_recv_op_common(sockfd, &code, &status); 1508c2ecf20Sopenharmony_ci if (rc < 0) { 1518c2ecf20Sopenharmony_ci err("Attach Request for %s failed - %s\n", 1528c2ecf20Sopenharmony_ci busid, usbip_op_common_status_string(status)); 1538c2ecf20Sopenharmony_ci return -1; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply)); 1578c2ecf20Sopenharmony_ci if (rc < 0) { 1588c2ecf20Sopenharmony_ci err("recv op_import_reply"); 1598c2ecf20Sopenharmony_ci return -1; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci PACK_OP_IMPORT_REPLY(0, &reply); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* check the reply */ 1658c2ecf20Sopenharmony_ci if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) { 1668c2ecf20Sopenharmony_ci err("recv different busid %s", reply.udev.busid); 1678c2ecf20Sopenharmony_ci return -1; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* import a device */ 1718c2ecf20Sopenharmony_ci return import_device(sockfd, &reply.udev); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int attach_device(char *host, char *busid) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int sockfd; 1778c2ecf20Sopenharmony_ci int rc; 1788c2ecf20Sopenharmony_ci int rhport; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci sockfd = usbip_net_tcp_connect(host, usbip_port_string); 1818c2ecf20Sopenharmony_ci if (sockfd < 0) { 1828c2ecf20Sopenharmony_ci err("tcp connect"); 1838c2ecf20Sopenharmony_ci return -1; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci rhport = query_import_device(sockfd, busid); 1878c2ecf20Sopenharmony_ci if (rhport < 0) 1888c2ecf20Sopenharmony_ci return -1; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci close(sockfd); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci rc = record_connection(host, usbip_port_string, busid, rhport); 1938c2ecf20Sopenharmony_ci if (rc < 0) { 1948c2ecf20Sopenharmony_ci err("record connection"); 1958c2ecf20Sopenharmony_ci return -1; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ciint usbip_attach(int argc, char *argv[]) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci static const struct option opts[] = { 2048c2ecf20Sopenharmony_ci { "remote", required_argument, NULL, 'r' }, 2058c2ecf20Sopenharmony_ci { "busid", required_argument, NULL, 'b' }, 2068c2ecf20Sopenharmony_ci { "device", required_argument, NULL, 'd' }, 2078c2ecf20Sopenharmony_ci { NULL, 0, NULL, 0 } 2088c2ecf20Sopenharmony_ci }; 2098c2ecf20Sopenharmony_ci char *host = NULL; 2108c2ecf20Sopenharmony_ci char *busid = NULL; 2118c2ecf20Sopenharmony_ci int opt; 2128c2ecf20Sopenharmony_ci int ret = -1; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (;;) { 2158c2ecf20Sopenharmony_ci opt = getopt_long(argc, argv, "d:r:b:", opts, NULL); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (opt == -1) 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci switch (opt) { 2218c2ecf20Sopenharmony_ci case 'r': 2228c2ecf20Sopenharmony_ci host = optarg; 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci case 'd': 2258c2ecf20Sopenharmony_ci case 'b': 2268c2ecf20Sopenharmony_ci busid = optarg; 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci default: 2298c2ecf20Sopenharmony_ci goto err_out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!host || !busid) 2348c2ecf20Sopenharmony_ci goto err_out; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = attach_device(host, busid); 2378c2ecf20Sopenharmony_ci goto out; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cierr_out: 2408c2ecf20Sopenharmony_ci usbip_attach_usage(); 2418c2ecf20Sopenharmony_ciout: 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci} 244