1e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney/*
2e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
3e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney *               2005-2007 Takahiro Hirofuchi
4e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney *
5e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * This program is free software: you can redistribute it and/or modify
6e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * it under the terms of the GNU General Public License as published by
7e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * the Free Software Foundation, either version 2 of the License, or
8e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * (at your option) any later version.
9e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney *
10e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * This program is distributed in the hope that it will be useful,
11e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * but WITHOUT ANY WARRANTY; without even the implied warranty of
12e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * GNU General Public License for more details.
14e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney *
15e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * You should have received a copy of the GNU General Public License
16e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney * along with this program. If not, see <http://www.gnu.org/licenses/>.
17e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney */
18e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
199cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney#include <sysfs/libsysfs.h>
209cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
219cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney#include <errno.h>
22e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney#include <stdio.h>
239cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney#include <string.h>
24e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
25e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney#include <getopt.h>
26e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
27e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney#include "usbip_common.h"
28e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney#include "utils.h"
29e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney#include "usbip.h"
30e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
31e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooneystatic const char usbip_unbind_usage_string[] =
32e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	"usbip unbind <args>\n"
33e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	"    -b, --busid=<busid>    Unbind " USBIP_HOST_DRV_NAME ".ko from "
34e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	"device on <busid>\n";
35e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
36e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooneyvoid usbip_unbind_usage(void)
37e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney{
38e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	printf("usage: %s", usbip_unbind_usage_string);
39e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney}
40e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
419cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooneystatic int unbind_device(char *busid)
42e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney{
439cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	char bus_type[] = "usb";
449cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	struct sysfs_driver *usbip_host_drv;
459cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	struct sysfs_device *dev;
469cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	struct dlist *devlist;
479cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	int verified = 0;
489cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	int rc, ret = -1;
499cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
509cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	char attr_name[] = "bConfigurationValue";
519cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	char sysfs_mntpath[SYSFS_PATH_MAX];
529cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	char busid_attr_path[SYSFS_PATH_MAX];
539cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	struct sysfs_attribute *busid_attr;
549cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	char *val = NULL;
559cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	int len;
569cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
579cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	/* verify the busid device is using usbip-host */
589cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	usbip_host_drv = sysfs_open_driver(bus_type, USBIP_HOST_DRV_NAME);
599cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (!usbip_host_drv) {
609cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("could not open %s driver: %s", USBIP_HOST_DRV_NAME,
619cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		    strerror(errno));
629cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		return -1;
639cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
649cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
659cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	devlist = sysfs_get_driver_devices(usbip_host_drv);
669cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (!devlist) {
679cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("%s is not in use by any devices", USBIP_HOST_DRV_NAME);
689cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		goto err_close_usbip_host_drv;
699cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
709cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
719cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	dlist_for_each_data(devlist, dev, struct sysfs_device) {
729cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		if (!strncmp(busid, dev->name, strlen(busid)) &&
739cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		    !strncmp(dev->driver_name, USBIP_HOST_DRV_NAME,
749cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney			     strlen(USBIP_HOST_DRV_NAME))) {
759cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney			verified = 1;
769cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney			break;
779cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		}
789cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
799cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
809cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (!verified) {
819cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("device on busid %s is not using %s", busid,
829cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		    USBIP_HOST_DRV_NAME);
839cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		goto err_close_usbip_host_drv;
849cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
859cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
869cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	/*
879cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 * NOTE: A read and write of an attribute value of the device busid
889cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 * refers to must be done to start probing. That way a rebind of the
899cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 * default driver for the device occurs.
909cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 *
919cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 * This seems very hackish and adds a lot of pointless code. I think it
929cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 * should be done in the kernel by the driver after del_match_busid is
939cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 * finished!
949cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	 */
959cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
969cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
979cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (rc < 0) {
989cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("sysfs must be mounted: %s", strerror(errno));
999cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		return -1;
1009cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
101e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
1029cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	snprintf(busid_attr_path, sizeof(busid_attr_path), "%s/%s/%s/%s/%s/%s",
1039cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		 sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DEVICES_NAME,
1049cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		 busid, attr_name);
1059cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
1069cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	/* read a device attribute */
1079cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	busid_attr = sysfs_open_attribute(busid_attr_path);
1089cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (!busid_attr) {
1099cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("could not open %s/%s: %s", busid, attr_name,
1109cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		    strerror(errno));
111e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		return -1;
112e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	}
113e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
1149cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (sysfs_read_attribute(busid_attr) < 0) {
1159cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("problem reading attribute: %s", strerror(errno));
1169cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		goto err_out;
1179cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
1189cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
1199cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	len = busid_attr->len;
1209cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	val = malloc(len);
1219cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	*val = *busid_attr->value;
1229cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	sysfs_close_attribute(busid_attr);
1239cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
1249cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	/* notify driver of unbind */
125e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	rc = modify_match_busid(busid, 0);
126e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	if (rc < 0) {
1279cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("unable to unbind device on %s", busid);
1289cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		goto err_out;
1299cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	}
1309cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
1319cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	/* write the device attribute */
1329cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	busid_attr = sysfs_open_attribute(busid_attr_path);
1339cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	if (!busid_attr) {
1349cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("could not open %s/%s: %s", busid, attr_name,
1359cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		    strerror(errno));
136e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		return -1;
137e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	}
138e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
1399cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	rc = sysfs_write_attribute(busid_attr, val, len);
140e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	if (rc < 0) {
1419cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		err("problem writing attribute: %s", strerror(errno));
1429cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		goto err_out;
143e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	}
1449cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	sysfs_close_attribute(busid_attr);
1459cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
1469cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	ret = 0;
1479cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	printf("unbind device on busid %s: complete\n", busid);
148e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
1499cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooneyerr_out:
1509cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	free(val);
1519cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooneyerr_close_usbip_host_drv:
1529cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	sysfs_close_driver(usbip_host_drv);
153e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
1549cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney	return ret;
155e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney}
156e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
157e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooneyint usbip_unbind(int argc, char *argv[])
158e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney{
159e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	static const struct option opts[] = {
160e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		{ "busid", required_argument, NULL, 'b' },
1619cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney		{ NULL,    0,                 NULL,  0  }
162e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	};
1639cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney
164e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	int opt;
165e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	int ret = -1;
166e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
167e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	for (;;) {
168e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		opt = getopt_long(argc, argv, "b:", opts, NULL);
169e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
170e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		if (opt == -1)
171e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney			break;
172e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
173e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		switch (opt) {
174e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		case 'b':
1759cda5704115d1611b408d8bd0e6e9dfd8a3617cbmatt mooney			ret = unbind_device(optarg);
176e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney			goto out;
177e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		default:
178e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney			goto err_out;
179e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney		}
180e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	}
181e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney
182e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooneyerr_out:
183e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	usbip_unbind_usage();
184e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooneyout:
185e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney	return ret;
186e9837bbb3e694eef4c55c934ebf1f8a0399b142cmatt mooney}
187