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 */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <ctype.h> 862306a36Sopenharmony_ci#include <limits.h> 962306a36Sopenharmony_ci#include <stdint.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <stdlib.h> 1262306a36Sopenharmony_ci#include <string.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <getopt.h> 1562306a36Sopenharmony_ci#include <unistd.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "vhci_driver.h" 1862306a36Sopenharmony_ci#include "usbip_common.h" 1962306a36Sopenharmony_ci#include "usbip_network.h" 2062306a36Sopenharmony_ci#include "usbip.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic const char usbip_detach_usage_string[] = 2362306a36Sopenharmony_ci "usbip detach <args>\n" 2462306a36Sopenharmony_ci " -p, --port=<port> " USBIP_VHCI_DRV_NAME 2562306a36Sopenharmony_ci " port the device is on\n"; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_civoid usbip_detach_usage(void) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci printf("usage: %s", usbip_detach_usage_string); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int detach_port(char *port) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int ret = 0; 3562306a36Sopenharmony_ci uint8_t portnum; 3662306a36Sopenharmony_ci char path[PATH_MAX+1]; 3762306a36Sopenharmony_ci int i; 3862306a36Sopenharmony_ci struct usbip_imported_device *idev; 3962306a36Sopenharmony_ci int found = 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci unsigned int port_len = strlen(port); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (unsigned int i = 0; i < port_len; i++) 4462306a36Sopenharmony_ci if (!isdigit(port[i])) { 4562306a36Sopenharmony_ci err("invalid port %s", port); 4662306a36Sopenharmony_ci return -1; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci portnum = atoi(port); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ret = usbip_vhci_driver_open(); 5262306a36Sopenharmony_ci if (ret < 0) { 5362306a36Sopenharmony_ci err("open vhci_driver (is vhci_hcd loaded?)"); 5462306a36Sopenharmony_ci return -1; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* check for invalid port */ 5862306a36Sopenharmony_ci for (i = 0; i < vhci_driver->nports; i++) { 5962306a36Sopenharmony_ci idev = &vhci_driver->idev[i]; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (idev->port == portnum) { 6262306a36Sopenharmony_ci found = 1; 6362306a36Sopenharmony_ci if (idev->status != VDEV_ST_NULL) 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci info("Port %d is already detached!\n", idev->port); 6662306a36Sopenharmony_ci goto call_driver_close; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!found) { 7162306a36Sopenharmony_ci err("Invalid port %s > maxports %d", 7262306a36Sopenharmony_ci port, vhci_driver->nports); 7362306a36Sopenharmony_ci goto call_driver_close; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* remove the port state file */ 7762306a36Sopenharmony_ci snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci remove(path); 8062306a36Sopenharmony_ci rmdir(VHCI_STATE_PATH); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = usbip_vhci_detach_device(portnum); 8362306a36Sopenharmony_ci if (ret < 0) { 8462306a36Sopenharmony_ci ret = -1; 8562306a36Sopenharmony_ci err("Port %d detach request failed!\n", portnum); 8662306a36Sopenharmony_ci goto call_driver_close; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci info("Port %d is now detached!\n", portnum); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cicall_driver_close: 9162306a36Sopenharmony_ci usbip_vhci_driver_close(); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return ret; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciint usbip_detach(int argc, char *argv[]) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci static const struct option opts[] = { 9962306a36Sopenharmony_ci { "port", required_argument, NULL, 'p' }, 10062306a36Sopenharmony_ci { NULL, 0, NULL, 0 } 10162306a36Sopenharmony_ci }; 10262306a36Sopenharmony_ci int opt; 10362306a36Sopenharmony_ci int ret = -1; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci for (;;) { 10662306a36Sopenharmony_ci opt = getopt_long(argc, argv, "p:", opts, NULL); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (opt == -1) 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci switch (opt) { 11262306a36Sopenharmony_ci case 'p': 11362306a36Sopenharmony_ci ret = detach_port(optarg); 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci default: 11662306a36Sopenharmony_ci goto err_out; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cierr_out: 12162306a36Sopenharmony_ci usbip_detach_usage(); 12262306a36Sopenharmony_ciout: 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci} 125