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 */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <ctype.h>
88c2ecf20Sopenharmony_ci#include <limits.h>
98c2ecf20Sopenharmony_ci#include <stdint.h>
108c2ecf20Sopenharmony_ci#include <stdio.h>
118c2ecf20Sopenharmony_ci#include <stdlib.h>
128c2ecf20Sopenharmony_ci#include <string.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <getopt.h>
158c2ecf20Sopenharmony_ci#include <unistd.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "vhci_driver.h"
188c2ecf20Sopenharmony_ci#include "usbip_common.h"
198c2ecf20Sopenharmony_ci#include "usbip_network.h"
208c2ecf20Sopenharmony_ci#include "usbip.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const char usbip_detach_usage_string[] =
238c2ecf20Sopenharmony_ci	"usbip detach <args>\n"
248c2ecf20Sopenharmony_ci	"    -p, --port=<port>    " USBIP_VHCI_DRV_NAME
258c2ecf20Sopenharmony_ci	" port the device is on\n";
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_civoid usbip_detach_usage(void)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	printf("usage: %s", usbip_detach_usage_string);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int detach_port(char *port)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	int ret = 0;
358c2ecf20Sopenharmony_ci	uint8_t portnum;
368c2ecf20Sopenharmony_ci	char path[PATH_MAX+1];
378c2ecf20Sopenharmony_ci	int i;
388c2ecf20Sopenharmony_ci	struct usbip_imported_device *idev;
398c2ecf20Sopenharmony_ci	int found = 0;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	unsigned int port_len = strlen(port);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	for (unsigned int i = 0; i < port_len; i++)
448c2ecf20Sopenharmony_ci		if (!isdigit(port[i])) {
458c2ecf20Sopenharmony_ci			err("invalid port %s", port);
468c2ecf20Sopenharmony_ci			return -1;
478c2ecf20Sopenharmony_ci		}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	portnum = atoi(port);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	ret = usbip_vhci_driver_open();
528c2ecf20Sopenharmony_ci	if (ret < 0) {
538c2ecf20Sopenharmony_ci		err("open vhci_driver");
548c2ecf20Sopenharmony_ci		return -1;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* check for invalid port */
588c2ecf20Sopenharmony_ci	for (i = 0; i < vhci_driver->nports; i++) {
598c2ecf20Sopenharmony_ci		idev = &vhci_driver->idev[i];
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		if (idev->port == portnum) {
628c2ecf20Sopenharmony_ci			found = 1;
638c2ecf20Sopenharmony_ci			if (idev->status != VDEV_ST_NULL)
648c2ecf20Sopenharmony_ci				break;
658c2ecf20Sopenharmony_ci			info("Port %d is already detached!\n", idev->port);
668c2ecf20Sopenharmony_ci			goto call_driver_close;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (!found) {
718c2ecf20Sopenharmony_ci		err("Invalid port %s > maxports %d",
728c2ecf20Sopenharmony_ci			port, vhci_driver->nports);
738c2ecf20Sopenharmony_ci		goto call_driver_close;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* remove the port state file */
778c2ecf20Sopenharmony_ci	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	remove(path);
808c2ecf20Sopenharmony_ci	rmdir(VHCI_STATE_PATH);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ret = usbip_vhci_detach_device(portnum);
838c2ecf20Sopenharmony_ci	if (ret < 0) {
848c2ecf20Sopenharmony_ci		ret = -1;
858c2ecf20Sopenharmony_ci		err("Port %d detach request failed!\n", portnum);
868c2ecf20Sopenharmony_ci		goto call_driver_close;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci	info("Port %d is now detached!\n", portnum);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cicall_driver_close:
918c2ecf20Sopenharmony_ci	usbip_vhci_driver_close();
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciint usbip_detach(int argc, char *argv[])
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	static const struct option opts[] = {
998c2ecf20Sopenharmony_ci		{ "port", required_argument, NULL, 'p' },
1008c2ecf20Sopenharmony_ci		{ NULL, 0, NULL, 0 }
1018c2ecf20Sopenharmony_ci	};
1028c2ecf20Sopenharmony_ci	int opt;
1038c2ecf20Sopenharmony_ci	int ret = -1;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for (;;) {
1068c2ecf20Sopenharmony_ci		opt = getopt_long(argc, argv, "p:", opts, NULL);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		if (opt == -1)
1098c2ecf20Sopenharmony_ci			break;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		switch (opt) {
1128c2ecf20Sopenharmony_ci		case 'p':
1138c2ecf20Sopenharmony_ci			ret = detach_port(optarg);
1148c2ecf20Sopenharmony_ci			goto out;
1158c2ecf20Sopenharmony_ci		default:
1168c2ecf20Sopenharmony_ci			goto err_out;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cierr_out:
1218c2ecf20Sopenharmony_ci	usbip_detach_usage();
1228c2ecf20Sopenharmony_ciout:
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
125