| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
| * 2005-2007 Takahiro Hirofuchi |
| */ |
| |
| #include <ctype.h> |
| #include <limits.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <getopt.h> |
| #include <unistd.h> |
| |
| #include "vhci_driver.h" |
| #include "usbip_common.h" |
| #include "usbip_network.h" |
| #include "usbip.h" |
| |
| static const char usbip_detach_usage_string[] = |
| "usbip detach <args>\n" |
| " -p, --port=<port> " USBIP_VHCI_DRV_NAME |
| " port the device is on\n"; |
| |
| void usbip_detach_usage(void) |
| { |
| printf("usage: %s", usbip_detach_usage_string); |
| } |
| |
| static int detach_port(char *port) |
| { |
| int ret = 0; |
| uint8_t portnum; |
| char path[PATH_MAX+1]; |
| int i; |
| struct usbip_imported_device *idev; |
| int found = 0; |
| |
| unsigned int port_len = strlen(port); |
| |
| for (unsigned int i = 0; i < port_len; i++) |
| if (!isdigit(port[i])) { |
| err("invalid port %s", port); |
| return -1; |
| } |
| |
| portnum = atoi(port); |
| |
| ret = usbip_vhci_driver_open(); |
| if (ret < 0) { |
| err("open vhci_driver (is vhci_hcd loaded?)"); |
| return -1; |
| } |
| |
| /* check for invalid port */ |
| for (i = 0; i < vhci_driver->nports; i++) { |
| idev = &vhci_driver->idev[i]; |
| |
| if (idev->port == portnum) { |
| found = 1; |
| if (idev->status != VDEV_ST_NULL) |
| break; |
| info("Port %d is already detached!\n", idev->port); |
| goto call_driver_close; |
| } |
| } |
| |
| if (!found) { |
| err("Invalid port %s > maxports %d", |
| port, vhci_driver->nports); |
| goto call_driver_close; |
| } |
| |
| /* remove the port state file */ |
| snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum); |
| |
| remove(path); |
| rmdir(VHCI_STATE_PATH); |
| |
| ret = usbip_vhci_detach_device(portnum); |
| if (ret < 0) { |
| ret = -1; |
| err("Port %d detach request failed!\n", portnum); |
| goto call_driver_close; |
| } |
| info("Port %d is now detached!\n", portnum); |
| |
| call_driver_close: |
| usbip_vhci_driver_close(); |
| |
| return ret; |
| } |
| |
| int usbip_detach(int argc, char *argv[]) |
| { |
| static const struct option opts[] = { |
| { "port", required_argument, NULL, 'p' }, |
| { NULL, 0, NULL, 0 } |
| }; |
| int opt; |
| int ret = -1; |
| |
| for (;;) { |
| opt = getopt_long(argc, argv, "p:", opts, NULL); |
| |
| if (opt == -1) |
| break; |
| |
| switch (opt) { |
| case 'p': |
| ret = detach_port(optarg); |
| goto out; |
| default: |
| goto err_out; |
| } |
| } |
| |
| err_out: |
| usbip_detach_usage(); |
| out: |
| return ret; |
| } |