forked from Qortal/Brooklyn
212 lines
4.5 KiB
C
212 lines
4.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
|
|
* 2005-2007 Takahiro Hirofuchi
|
|
*/
|
|
|
|
#include <libudev.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include "usbip_common.h"
|
|
#include "utils.h"
|
|
#include "usbip.h"
|
|
#include "sysfs_utils.h"
|
|
|
|
enum unbind_status {
|
|
UNBIND_ST_OK,
|
|
UNBIND_ST_USBIP_HOST,
|
|
UNBIND_ST_FAILED
|
|
};
|
|
|
|
static const char usbip_bind_usage_string[] =
|
|
"usbip bind <args>\n"
|
|
" -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
|
|
"on <busid>\n";
|
|
|
|
void usbip_bind_usage(void)
|
|
{
|
|
printf("usage: %s", usbip_bind_usage_string);
|
|
}
|
|
|
|
/* call at unbound state */
|
|
static int bind_usbip(char *busid)
|
|
{
|
|
char attr_name[] = "bind";
|
|
char bind_attr_path[SYSFS_PATH_MAX];
|
|
int rc = -1;
|
|
|
|
snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
|
|
SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
|
|
SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
|
|
|
|
rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid));
|
|
if (rc < 0) {
|
|
err("error binding device %s to driver: %s", busid,
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* buggy driver may cause dead lock */
|
|
static int unbind_other(char *busid)
|
|
{
|
|
enum unbind_status status = UNBIND_ST_OK;
|
|
|
|
char attr_name[] = "unbind";
|
|
char unbind_attr_path[SYSFS_PATH_MAX];
|
|
int rc = -1;
|
|
|
|
struct udev *udev;
|
|
struct udev_device *dev;
|
|
const char *driver;
|
|
const char *bDevClass;
|
|
|
|
/* Create libudev context. */
|
|
udev = udev_new();
|
|
|
|
/* Get the device. */
|
|
dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
|
|
if (!dev) {
|
|
dbg("unable to find device with bus ID %s", busid);
|
|
goto err_close_busid_dev;
|
|
}
|
|
|
|
/* Check what kind of device it is. */
|
|
bDevClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
|
|
if (!bDevClass) {
|
|
dbg("unable to get bDevClass device attribute");
|
|
goto err_close_busid_dev;
|
|
}
|
|
|
|
if (!strncmp(bDevClass, "09", strlen(bDevClass))) {
|
|
dbg("skip unbinding of hub");
|
|
goto err_close_busid_dev;
|
|
}
|
|
|
|
/* Get the device driver. */
|
|
driver = udev_device_get_driver(dev);
|
|
if (!driver) {
|
|
/* No driver bound to this device. */
|
|
goto out;
|
|
}
|
|
|
|
if (!strncmp(USBIP_HOST_DRV_NAME, driver,
|
|
strlen(USBIP_HOST_DRV_NAME))) {
|
|
/* Already bound to usbip-host. */
|
|
status = UNBIND_ST_USBIP_HOST;
|
|
goto out;
|
|
}
|
|
|
|
/* Unbind device from driver. */
|
|
snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
|
|
SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
|
|
SYSFS_DRIVERS_NAME, driver, attr_name);
|
|
|
|
rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
|
|
if (rc < 0) {
|
|
err("error unbinding device %s from driver", busid);
|
|
goto err_close_busid_dev;
|
|
}
|
|
|
|
goto out;
|
|
|
|
err_close_busid_dev:
|
|
status = UNBIND_ST_FAILED;
|
|
out:
|
|
udev_device_unref(dev);
|
|
udev_unref(udev);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int bind_device(char *busid)
|
|
{
|
|
int rc;
|
|
struct udev *udev;
|
|
struct udev_device *dev;
|
|
const char *devpath;
|
|
|
|
/* Check whether the device with this bus ID exists. */
|
|
udev = udev_new();
|
|
dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
|
|
if (!dev) {
|
|
err("device with the specified bus ID does not exist");
|
|
return -1;
|
|
}
|
|
devpath = udev_device_get_devpath(dev);
|
|
udev_unref(udev);
|
|
|
|
/* If the device is already attached to vhci_hcd - bail out */
|
|
if (strstr(devpath, USBIP_VHCI_DRV_NAME)) {
|
|
err("bind loop detected: device: %s is attached to %s\n",
|
|
devpath, USBIP_VHCI_DRV_NAME);
|
|
return -1;
|
|
}
|
|
|
|
rc = unbind_other(busid);
|
|
if (rc == UNBIND_ST_FAILED) {
|
|
err("could not unbind driver from device on busid %s", busid);
|
|
return -1;
|
|
} else if (rc == UNBIND_ST_USBIP_HOST) {
|
|
err("device on busid %s is already bound to %s", busid,
|
|
USBIP_HOST_DRV_NAME);
|
|
return -1;
|
|
}
|
|
|
|
rc = modify_match_busid(busid, 1);
|
|
if (rc < 0) {
|
|
err("unable to bind device on %s", busid);
|
|
return -1;
|
|
}
|
|
|
|
rc = bind_usbip(busid);
|
|
if (rc < 0) {
|
|
err("could not bind device to %s", USBIP_HOST_DRV_NAME);
|
|
modify_match_busid(busid, 0);
|
|
return -1;
|
|
}
|
|
|
|
info("bind device on busid %s: complete", busid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usbip_bind(int argc, char *argv[])
|
|
{
|
|
static const struct option opts[] = {
|
|
{ "busid", required_argument, NULL, 'b' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
int opt;
|
|
int ret = -1;
|
|
|
|
for (;;) {
|
|
opt = getopt_long(argc, argv, "b:", opts, NULL);
|
|
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case 'b':
|
|
ret = bind_device(optarg);
|
|
goto out;
|
|
default:
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
err_out:
|
|
usbip_bind_usage();
|
|
out:
|
|
return ret;
|
|
}
|