1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
4 | * 2005-2007 Takahiro Hirofuchi |
5 | */ |
6 | |
7 | #include <ctype.h> |
8 | #include <limits.h> |
9 | #include <stdint.h> |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | |
14 | #include <getopt.h> |
15 | #include <unistd.h> |
16 | |
17 | #include "vhci_driver.h" |
18 | #include "usbip_common.h" |
19 | #include "usbip_network.h" |
20 | #include "usbip.h" |
21 | |
22 | static const char usbip_detach_usage_string[] = |
23 | "usbip detach <args>\n" |
24 | " -p, --port=<port> " USBIP_VHCI_DRV_NAME |
25 | " port the device is on\n" ; |
26 | |
27 | void usbip_detach_usage(void) |
28 | { |
29 | printf("usage: %s" , usbip_detach_usage_string); |
30 | } |
31 | |
32 | static int detach_port(char *port) |
33 | { |
34 | int ret = 0; |
35 | uint8_t portnum; |
36 | char path[PATH_MAX+1]; |
37 | int i; |
38 | struct usbip_imported_device *idev; |
39 | int found = 0; |
40 | |
41 | unsigned int port_len = strlen(port); |
42 | |
43 | for (unsigned int i = 0; i < port_len; i++) |
44 | if (!isdigit(port[i])) { |
45 | err("invalid port %s" , port); |
46 | return -1; |
47 | } |
48 | |
49 | portnum = atoi(port); |
50 | |
51 | ret = usbip_vhci_driver_open(); |
52 | if (ret < 0) { |
53 | err("open vhci_driver (is vhci_hcd loaded?)" ); |
54 | return -1; |
55 | } |
56 | |
57 | /* check for invalid port */ |
58 | for (i = 0; i < vhci_driver->nports; i++) { |
59 | idev = &vhci_driver->idev[i]; |
60 | |
61 | if (idev->port == portnum) { |
62 | found = 1; |
63 | if (idev->status != VDEV_ST_NULL) |
64 | break; |
65 | info("Port %d is already detached!\n" , idev->port); |
66 | goto call_driver_close; |
67 | } |
68 | } |
69 | |
70 | if (!found) { |
71 | err("Invalid port %s > maxports %d" , |
72 | port, vhci_driver->nports); |
73 | goto call_driver_close; |
74 | } |
75 | |
76 | /* remove the port state file */ |
77 | snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d" , portnum); |
78 | |
79 | remove(path); |
80 | rmdir(VHCI_STATE_PATH); |
81 | |
82 | ret = usbip_vhci_detach_device(portnum); |
83 | if (ret < 0) { |
84 | ret = -1; |
85 | err("Port %d detach request failed!\n" , portnum); |
86 | goto call_driver_close; |
87 | } |
88 | info("Port %d is now detached!\n" , portnum); |
89 | |
90 | call_driver_close: |
91 | usbip_vhci_driver_close(); |
92 | |
93 | return ret; |
94 | } |
95 | |
96 | int usbip_detach(int argc, char *argv[]) |
97 | { |
98 | static const struct option opts[] = { |
99 | { "port" , required_argument, NULL, 'p' }, |
100 | { NULL, 0, NULL, 0 } |
101 | }; |
102 | int opt; |
103 | int ret = -1; |
104 | |
105 | for (;;) { |
106 | opt = getopt_long(argc, argv, "p:" , opts, NULL); |
107 | |
108 | if (opt == -1) |
109 | break; |
110 | |
111 | switch (opt) { |
112 | case 'p': |
113 | ret = detach_port(port: optarg); |
114 | goto out; |
115 | default: |
116 | goto err_out; |
117 | } |
118 | } |
119 | |
120 | err_out: |
121 | usbip_detach_usage(); |
122 | out: |
123 | return ret; |
124 | } |
125 | |