162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 462306a36Sopenharmony_ci * 2005-2007 Takahiro Hirofuchi 562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics 662306a36Sopenharmony_ci * Igor Kotrasinski <i.kotrasinsk@samsung.com> 762306a36Sopenharmony_ci * Krzysztof Opasiak <k.opasiak@samsung.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <sys/stat.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <limits.h> 1362306a36Sopenharmony_ci#include <stdint.h> 1462306a36Sopenharmony_ci#include <stdio.h> 1562306a36Sopenharmony_ci#include <string.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <fcntl.h> 1862306a36Sopenharmony_ci#include <getopt.h> 1962306a36Sopenharmony_ci#include <unistd.h> 2062306a36Sopenharmony_ci#include <errno.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "vhci_driver.h" 2362306a36Sopenharmony_ci#include "usbip_common.h" 2462306a36Sopenharmony_ci#include "usbip_network.h" 2562306a36Sopenharmony_ci#include "usbip.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const char usbip_attach_usage_string[] = 2862306a36Sopenharmony_ci "usbip attach <args>\n" 2962306a36Sopenharmony_ci " -r, --remote=<host> The machine with exported USB devices\n" 3062306a36Sopenharmony_ci " -b, --busid=<busid> Busid of the device on <host>\n" 3162306a36Sopenharmony_ci " -d, --device=<devid> Id of the virtual UDC on <host>\n"; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid usbip_attach_usage(void) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci printf("usage: %s", usbip_attach_usage_string); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MAX_BUFF 100 3962306a36Sopenharmony_cistatic int record_connection(char *host, char *port, char *busid, int rhport) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int fd; 4262306a36Sopenharmony_ci char path[PATH_MAX+1]; 4362306a36Sopenharmony_ci char buff[MAX_BUFF+1]; 4462306a36Sopenharmony_ci int ret; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ret = mkdir(VHCI_STATE_PATH, 0700); 4762306a36Sopenharmony_ci if (ret < 0) { 4862306a36Sopenharmony_ci /* if VHCI_STATE_PATH exists, then it better be a directory */ 4962306a36Sopenharmony_ci if (errno == EEXIST) { 5062306a36Sopenharmony_ci struct stat s; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ret = stat(VHCI_STATE_PATH, &s); 5362306a36Sopenharmony_ci if (ret < 0) 5462306a36Sopenharmony_ci return -1; 5562306a36Sopenharmony_ci if (!(s.st_mode & S_IFDIR)) 5662306a36Sopenharmony_ci return -1; 5762306a36Sopenharmony_ci } else 5862306a36Sopenharmony_ci return -1; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); 6462306a36Sopenharmony_ci if (fd < 0) 6562306a36Sopenharmony_ci return -1; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci snprintf(buff, MAX_BUFF, "%s %s %s\n", 6862306a36Sopenharmony_ci host, port, busid); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ret = write(fd, buff, strlen(buff)); 7162306a36Sopenharmony_ci if (ret != (ssize_t) strlen(buff)) { 7262306a36Sopenharmony_ci close(fd); 7362306a36Sopenharmony_ci return -1; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci close(fd); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int import_device(int sockfd, struct usbip_usb_device *udev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int rc; 8462306a36Sopenharmony_ci int port; 8562306a36Sopenharmony_ci uint32_t speed = udev->speed; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci rc = usbip_vhci_driver_open(); 8862306a36Sopenharmony_ci if (rc < 0) { 8962306a36Sopenharmony_ci err("open vhci_driver (is vhci_hcd loaded?)"); 9062306a36Sopenharmony_ci goto err_out; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci do { 9462306a36Sopenharmony_ci port = usbip_vhci_get_free_port(speed); 9562306a36Sopenharmony_ci if (port < 0) { 9662306a36Sopenharmony_ci err("no free port"); 9762306a36Sopenharmony_ci goto err_driver_close; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci dbg("got free port %d", port); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci rc = usbip_vhci_attach_device(port, sockfd, udev->busnum, 10362306a36Sopenharmony_ci udev->devnum, udev->speed); 10462306a36Sopenharmony_ci if (rc < 0 && errno != EBUSY) { 10562306a36Sopenharmony_ci err("import device"); 10662306a36Sopenharmony_ci goto err_driver_close; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } while (rc < 0); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci usbip_vhci_driver_close(); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return port; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cierr_driver_close: 11562306a36Sopenharmony_ci usbip_vhci_driver_close(); 11662306a36Sopenharmony_cierr_out: 11762306a36Sopenharmony_ci return -1; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int query_import_device(int sockfd, char *busid) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int rc; 12362306a36Sopenharmony_ci struct op_import_request request; 12462306a36Sopenharmony_ci struct op_import_reply reply; 12562306a36Sopenharmony_ci uint16_t code = OP_REP_IMPORT; 12662306a36Sopenharmony_ci int status; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci memset(&request, 0, sizeof(request)); 12962306a36Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* send a request */ 13262306a36Sopenharmony_ci rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0); 13362306a36Sopenharmony_ci if (rc < 0) { 13462306a36Sopenharmony_ci err("send op_common"); 13562306a36Sopenharmony_ci return -1; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci PACK_OP_IMPORT_REQUEST(0, &request); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci rc = usbip_net_send(sockfd, (void *) &request, sizeof(request)); 14362306a36Sopenharmony_ci if (rc < 0) { 14462306a36Sopenharmony_ci err("send op_import_request"); 14562306a36Sopenharmony_ci return -1; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* receive a reply */ 14962306a36Sopenharmony_ci rc = usbip_net_recv_op_common(sockfd, &code, &status); 15062306a36Sopenharmony_ci if (rc < 0) { 15162306a36Sopenharmony_ci err("Attach Request for %s failed - %s\n", 15262306a36Sopenharmony_ci busid, usbip_op_common_status_string(status)); 15362306a36Sopenharmony_ci return -1; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply)); 15762306a36Sopenharmony_ci if (rc < 0) { 15862306a36Sopenharmony_ci err("recv op_import_reply"); 15962306a36Sopenharmony_ci return -1; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci PACK_OP_IMPORT_REPLY(0, &reply); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* check the reply */ 16562306a36Sopenharmony_ci if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) { 16662306a36Sopenharmony_ci err("recv different busid %s", reply.udev.busid); 16762306a36Sopenharmony_ci return -1; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* import a device */ 17162306a36Sopenharmony_ci return import_device(sockfd, &reply.udev); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int attach_device(char *host, char *busid) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci int sockfd; 17762306a36Sopenharmony_ci int rc; 17862306a36Sopenharmony_ci int rhport; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci sockfd = usbip_net_tcp_connect(host, usbip_port_string); 18162306a36Sopenharmony_ci if (sockfd < 0) { 18262306a36Sopenharmony_ci err("tcp connect"); 18362306a36Sopenharmony_ci return -1; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci rhport = query_import_device(sockfd, busid); 18762306a36Sopenharmony_ci if (rhport < 0) 18862306a36Sopenharmony_ci return -1; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci close(sockfd); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci rc = record_connection(host, usbip_port_string, busid, rhport); 19362306a36Sopenharmony_ci if (rc < 0) { 19462306a36Sopenharmony_ci err("record connection"); 19562306a36Sopenharmony_ci return -1; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciint usbip_attach(int argc, char *argv[]) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci static const struct option opts[] = { 20462306a36Sopenharmony_ci { "remote", required_argument, NULL, 'r' }, 20562306a36Sopenharmony_ci { "busid", required_argument, NULL, 'b' }, 20662306a36Sopenharmony_ci { "device", required_argument, NULL, 'd' }, 20762306a36Sopenharmony_ci { NULL, 0, NULL, 0 } 20862306a36Sopenharmony_ci }; 20962306a36Sopenharmony_ci char *host = NULL; 21062306a36Sopenharmony_ci char *busid = NULL; 21162306a36Sopenharmony_ci int opt; 21262306a36Sopenharmony_ci int ret = -1; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci for (;;) { 21562306a36Sopenharmony_ci opt = getopt_long(argc, argv, "d:r:b:", opts, NULL); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (opt == -1) 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci switch (opt) { 22162306a36Sopenharmony_ci case 'r': 22262306a36Sopenharmony_ci host = optarg; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci case 'd': 22562306a36Sopenharmony_ci case 'b': 22662306a36Sopenharmony_ci busid = optarg; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci default: 22962306a36Sopenharmony_ci goto err_out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (!host || !busid) 23462306a36Sopenharmony_ci goto err_out; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = attach_device(host, busid); 23762306a36Sopenharmony_ci goto out; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cierr_out: 24062306a36Sopenharmony_ci usbip_attach_usage(); 24162306a36Sopenharmony_ciout: 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci} 244