Commit e9837bbb authored by matt mooney's avatar matt mooney Committed by Greg Kroah-Hartman

staging: usbip: userspace tools v1.0.0

The new and improved (well somewhat, with a ways to go) userspace utility.

    mfm:pts/8[~/tmp/userspace]
    May26 05:18:31 % ./src/usbip help
    usage: usbip [--debug] [version]
                 [help] <command> <args>

      attach     Attach a remote USB device
      detach     Detach a remote USB device
      list       List exported or local USB devices
      bind       Bind device to usbip-host.ko
      unbind     Unbind device from usbip-host.ko

This first commit of the userspace `usbip' utility uses to same
implementation as the old tools, `usbip' and  `usbip_bind_driver'.
Nothing significant has changed so compatibility with windows has
_not_ been broken. However, the tools remain broken in many ways
due to the old implementation.
Signed-off-by: default avatarmatt mooney <mfm@muteddisk.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 58058422
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59) AC_PREREQ(2.59)
AC_INIT([usbip], [0.1.8], [usbip-devel@lists.sourceforge.net]) AC_INIT([usbip], [1.0.0], [usbip-devel@lists.sourceforge.net])
AC_DEFINE([USBIP_VERSION], [0x000106], [numeric version number]) AC_DEFINE([USBIP_VERSION], [0x00000100], [binary-coded decimal version number])
CURRENT=0 CURRENT=0
REVISION=1 REVISION=1
......
...@@ -2,9 +2,10 @@ AM_CPPFLAGS := -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"' ...@@ -2,9 +2,10 @@ AM_CPPFLAGS := -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
AM_CFLAGS := @EXTRA_CFLAGS@ @PACKAGE_CFLAGS@ AM_CFLAGS := @EXTRA_CFLAGS@ @PACKAGE_CFLAGS@
LDADD := $(top_srcdir)/libsrc/libusbip.la @PACKAGE_LIBS@ LDADD := $(top_srcdir)/libsrc/libusbip.la @PACKAGE_LIBS@
sbin_PROGRAMS := usbip usbipd usbip_bind_driver sbin_PROGRAMS := usbip usbipd
usbip_SOURCES := usbip.c usbip_network.c usbip_network.h usbip_SOURCES := usbip.c utils.c usbip_network.c \
usbipd_SOURCES := usbipd.c usbip_network.c usbip_network.h usbip_attach.c usbip_detach.c usbip_list.c \
usbip_bind_driver_SOURCES := bind-driver.c utils.c utils.h \ usbip_bind.c usbip_unbind.c
usbip_network.h usbip_network.c
usbipd_SOURCES := usbipd.c usbip_network.c
/*
*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <glib.h>
#include "usbip.h"
#include "utils.h"
static const struct option longopts[] = {
{"usbip", required_argument, NULL, 'u'},
{"other", required_argument, NULL, 'o'},
{"list", no_argument, NULL, 'l'},
{"list2", no_argument, NULL, 'L'},
{"help", no_argument, NULL, 'h'},
#if 0
{"allusbip", no_argument, NULL, 'a'},
{"export-to", required_argument, NULL, 'e'},
{"unexport", required_argument, NULL, 'x'},
{"busid", required_argument, NULL, 'b'},
#endif
{NULL, 0, NULL, 0}
};
static void show_help(void)
{
printf("Usage: usbip_bind_driver [OPTION]\n");
printf("Change driver binding for USB/IP.\n");
printf(" --usbip busid make a device exportable\n");
printf(" --other busid use a device by a local driver\n");
printf(" --list print usb devices and their drivers\n");
printf(" --list2 print usb devices and their drivers in parseable mode\n");
#if 0
printf(" --allusbip make all devices exportable\n");
printf(" --export-to host export the device to 'host'\n");
printf(" --unexport host unexport a device previously exported to 'host'\n");
printf(" --busid busid the busid used for --export-to\n");
#endif
}
static int modify_match_busid(char *busid, int add)
{
int fd;
int ret;
char buff[BUS_ID_SIZE + 4];
char sysfs_mntpath[SYSFS_PATH_MAX];
char match_busid_path[SYSFS_PATH_MAX];
ret = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (ret < 0) {
err("sysfs must be mounted");
return -1;
}
snprintf(match_busid_path, sizeof(match_busid_path),
"%s/%s/usb/%s/%s/match_busid", sysfs_mntpath, SYSFS_BUS_NAME,
SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME);
/* BUS_IS_SIZE includes NULL termination? */
if (strnlen(busid, BUS_ID_SIZE) > BUS_ID_SIZE - 1) {
g_warning("too long busid");
return -1;
}
fd = open(match_busid_path, O_WRONLY);
if (fd < 0)
return -1;
if (add)
snprintf(buff, BUS_ID_SIZE + 4, "add %s", busid);
else
snprintf(buff, BUS_ID_SIZE + 4, "del %s", busid);
g_debug("write \"%s\" to %s", buff, match_busid_path);
ret = write(fd, buff, sizeof(buff));
if (ret < 0) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static const char unbind_path_format[] = "/sys/bus/usb/devices/%s/driver/unbind";
/* buggy driver may cause dead lock */
static int unbind_interface_busid(char *busid)
{
char unbind_path[PATH_MAX];
int fd;
int ret;
snprintf(unbind_path, sizeof(unbind_path), unbind_path_format, busid);
fd = open(unbind_path, O_WRONLY);
if (fd < 0) {
g_warning("opening unbind_path failed: %d", fd);
return -1;
}
ret = write(fd, busid, strnlen(busid, BUS_ID_SIZE));
if (ret < 0) {
g_warning("write to unbind_path failed: %d", ret);
close(fd);
return -1;
}
close(fd);
return 0;
}
static int unbind_interface(char *busid, int configvalue, int interface)
{
char inf_busid[BUS_ID_SIZE];
g_debug("unbinding interface");
snprintf(inf_busid, BUS_ID_SIZE, "%s:%d.%d", busid, configvalue, interface);
return unbind_interface_busid(inf_busid);
}
static const char bind_path_format[] = "/sys/bus/usb/drivers/%s/bind";
static int bind_interface_busid(char *busid, char *driver)
{
char bind_path[PATH_MAX];
int fd;
int ret;
snprintf(bind_path, sizeof(bind_path), bind_path_format, driver);
fd = open(bind_path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, busid, strnlen(busid, BUS_ID_SIZE));
if (ret < 0) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int bind_interface(char *busid, int configvalue, int interface, char *driver)
{
char inf_busid[BUS_ID_SIZE];
snprintf(inf_busid, BUS_ID_SIZE, "%s:%d.%d", busid, configvalue, interface);
return bind_interface_busid(inf_busid, driver);
}
static int unbind(char *busid)
{
int configvalue = 0;
int ninterface = 0;
int devclass = 0;
int i;
int failed = 0;
configvalue = read_bConfigurationValue(busid);
ninterface = read_bNumInterfaces(busid);
devclass = read_bDeviceClass(busid);
if (configvalue < 0 || ninterface < 0 || devclass < 0) {
g_warning("read config and ninf value, removed?");
return -1;
}
if (devclass == 0x09) {
g_message("skip unbinding of hub");
return -1;
}
for (i = 0; i < ninterface; i++) {
char driver[PATH_MAX];
int ret;
bzero(&driver, sizeof(driver));
getdriver(busid, configvalue, i, driver, PATH_MAX-1);
g_debug(" %s:%d.%d -> %s ", busid, configvalue, i, driver);
if (!strncmp("none", driver, PATH_MAX))
continue; /* unbound interface */
#if 0
if (!strncmp("usbip", driver, PATH_MAX))
continue; /* already bound to usbip */
#endif
/* unbinding */
ret = unbind_interface(busid, configvalue, i);
if (ret < 0) {
g_warning("unbind driver at %s:%d.%d failed",
busid, configvalue, i);
failed = 1;
}
}
if (failed)
return -1;
else
return 0;
}
/* call at unbound state */
static int bind_to_usbip(char *busid)
{
int configvalue = 0;
int ninterface = 0;
int i;
int failed = 0;
configvalue = read_bConfigurationValue(busid);
ninterface = read_bNumInterfaces(busid);
if (configvalue < 0 || ninterface < 0) {
g_warning("read config and ninf value, removed?");
return -1;
}
for (i = 0; i < ninterface; i++) {
int ret;
ret = bind_interface(busid, configvalue, i,
USBIP_HOST_DRV_NAME);
if (ret < 0) {
g_warning("bind usbip at %s:%d.%d, failed",
busid, configvalue, i);
failed = 1;
/* need to contine binding at other interfaces */
}
}
if (failed)
return -1;
else
return 0;
}
static int use_device_by_usbip(char *busid)
{
int ret;
ret = unbind(busid);
if (ret < 0) {
g_warning("unbind drivers of %s, failed", busid);
return -1;
}
ret = modify_match_busid(busid, 1);
if (ret < 0) {
g_warning("add %s to match_busid, failed", busid);
return -1;
}
ret = bind_to_usbip(busid);
if (ret < 0) {
g_warning("bind usbip to %s, failed", busid);
modify_match_busid(busid, 0);
return -1;
}
g_message("bind %s to usbip, complete!", busid);
return 0;
}
static int use_device_by_other(char *busid)
{
int ret;
int config;
/* read and write the same config value to kick probing */
config = read_bConfigurationValue(busid);
if (config < 0) {
g_warning("read bConfigurationValue of %s, failed", busid);
return -1;
}
ret = modify_match_busid(busid, 0);
if (ret < 0) {
g_warning("del %s to match_busid, failed", busid);
return -1;
}
ret = write_bConfigurationValue(busid, config);
if (ret < 0) {
g_warning("read bConfigurationValue of %s, failed", busid);
return -1;
}
g_message("bind %s to other drivers than usbip, complete!", busid);
return 0;
}
#include <sys/types.h>
#include <regex.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
static int is_usb_device(char *busid)
{
int ret;
regex_t regex;
regmatch_t pmatch[1];
ret = regcomp(&regex, "^[0-9]+-[0-9]+(\\.[0-9]+)*$", REG_NOSUB|REG_EXTENDED);
if (ret < 0)
g_error("regcomp: %s\n", strerror(errno));
ret = regexec(&regex, busid, 0, pmatch, 0);
if (ret)
return 0; /* not matched */
return 1;
}
#include <dirent.h>
static int show_devices(void)
{
DIR *dir;
dir = opendir("/sys/bus/usb/devices/");
if (!dir)
g_error("opendir: %s", strerror(errno));
printf("List USB devices\n");
for (;;) {
struct dirent *dirent;
char *busid;
dirent = readdir(dir);
if (!dirent)
break;
busid = dirent->d_name;
if (is_usb_device(busid)) {
char name[100] = {'\0'};
char driver[100] = {'\0'};
int conf, ninf = 0;
int i;
conf = read_bConfigurationValue(busid);
ninf = read_bNumInterfaces(busid);
getdevicename(busid, name, sizeof(name));
printf(" - busid %s (%s)\n", busid, name);
for (i = 0; i < ninf; i++) {
getdriver(busid, conf, i, driver, sizeof(driver));
printf(" %s:%d.%d -> %s\n", busid, conf, i, driver);
}
printf("\n");
}
}
closedir(dir);
return 0;
}
static int show_devices2(void)
{
DIR *dir;
dir = opendir("/sys/bus/usb/devices/");
if (!dir)
g_error("opendir: %s", strerror(errno));
for (;;) {
struct dirent *dirent;
char *busid;
dirent = readdir(dir);
if (!dirent)
break;
busid = dirent->d_name;
if (is_usb_device(busid)) {
char name[100] = {'\0'};
char driver[100] = {'\0'};
int conf, ninf = 0;
int i;
conf = read_bConfigurationValue(busid);
ninf = read_bNumInterfaces(busid);
getdevicename(busid, name, sizeof(name));
printf("busid=%s#usbid=%s#", busid, name);
for (i = 0; i < ninf; i++) {
getdriver(busid, conf, i, driver, sizeof(driver));
printf("%s:%d.%d=%s#", busid, conf, i, driver);
}
printf("\n");
}
}
closedir(dir);
return 0;
}
#if 0
static int export_to(char *host, char *busid) {
int ret;
if( host == NULL ) {
printf( "no host given\n\n");
show_help();
return -1;
}
if( busid == NULL ) {
/* XXX print device list and ask for busnumber, if none is
* given */
printf( "no busid given, use --busid switch\n\n");
show_help();
return -1;
}
ret = use_device_by_usbip(busid);
if( ret != 0 ) {
printf( "could not bind driver to usbip\n");
return -1;
}
printf( "DEBUG: exporting device '%s' to '%s'\n", busid, host );
ret = export_busid_to_host(host, busid); /* usbip_export.[ch] */
if( ret != 0 ) {
printf( "could not export device to host\n" );
printf( " host: %s, device: %s\n", host, busid );
use_device_by_other(busid);
return -1;
}
return 0;
}
static int unexport_from(char *host, char *busid) {
int ret;
if (!host || !busid)
g_error("no host or no busid\n");
g_message("unexport_from: host: '%s', busid: '%s'", host, busid);
ret = unexport_busid_from_host(host, busid); /* usbip_export.[ch] */
if( ret != 0 ) {
err( "could not unexport device from host\n" );
err( " host: %s, device: %s\n", host, busid );
}
ret = use_device_by_other(busid);
if (ret < 0)
g_error("could not unbind device from usbip\n");
return 0;
}
static int allusbip(void)
{
DIR *dir;
dir = opendir("/sys/bus/usb/devices/");
if (!dir)
g_error("opendir: %s", strerror(errno));
for (;;) {
struct dirent *dirent;
char *busid;
dirent = readdir(dir);
if (!dirent)
break;
busid = dirent->d_name;
if (!is_usb_device(busid))
continue;
{
char name[PATH_MAX];
int conf, ninf = 0;
int i;
int be_local = 0;
conf = read_bConfigurationValue(busid);
ninf = read_bNumInterfaces(busid);
getdevicename(busid, name, sizeof(name));
for (i = 0; i < ninf; i++) {
char driver[PATH_MAX];
getdriver(busid, conf, i, driver, sizeof(driver));
#if 0
if (strncmp(driver, "usbhid", 6) == 0 || strncmp(driver, "usb-storage", 11) == 0) {
be_local = 1;
break;
}
#endif
}
if (be_local == 0)
use_device_by_usbip(busid);
}
}
closedir(dir);
return 0;
}
#endif
int main(int argc, char **argv)
{
char *busid = NULL;
char *remote_host __attribute__((unused)) = NULL;
enum {
cmd_unknown = 0,
cmd_use_by_usbip,
cmd_use_by_other,
cmd_list,
cmd_list2,
cmd_allusbip,
cmd_export_to,
cmd_unexport,
cmd_help,
} cmd = cmd_unknown;
if (geteuid() != 0)
g_warning("running non-root?");
for (;;) {
int c;
int index = 0;
c = getopt_long(argc, argv, "u:o:hlLae:x:b:", longopts, &index);
if (c == -1)
break;
switch (c) {
case 'u':
cmd = cmd_use_by_usbip;
busid = optarg;
break;
case 'o' :
cmd = cmd_use_by_other;
busid = optarg;
break;
case 'l' :
cmd = cmd_list;
break;
case 'L' :
cmd = cmd_list2;
break;
case 'a' :
cmd = cmd_allusbip;
break;
case 'b':
busid = optarg;
break;
case 'e':
cmd = cmd_export_to;
remote_host = optarg;
break;
case 'x':
cmd = cmd_unexport;
remote_host = optarg;
break;
case 'h': /* fallthrough */
case '?':
cmd = cmd_help;
break;
default:
g_error("getopt");
}
//if (cmd)
// break;
}
switch (cmd) {
case cmd_use_by_usbip:
use_device_by_usbip(busid);
break;
case cmd_use_by_other:
use_device_by_other(busid);
break;
case cmd_list:
show_devices();
break;
case cmd_list2:
show_devices2();
break;
#if 0
case cmd_allusbip:
allusbip();
break;
case cmd_export_to:
export_to(remote_host, busid);
break;
case cmd_unexport:
unexport_from(remote_host, busid);
break;
#endif
case cmd_help: /* fallthrough */
case cmd_unknown:
show_help();
break;
default:
g_error("NOT REACHED");
}
return 0;
}
/* /*
* command structure borrowed from udev
* (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
* *
* Copyright (C) 2005-2007 Takahiro Hirofuchi * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef HAVE_CONFIG_H #include <stdio.h>
#include "../config.h"
#endif
#include "usbip.h"
#include "usbip_network.h"
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h>
#include <glib.h>
static const char version[] = PACKAGE_STRING;
/* /sys/devices/platform/vhci_hcd/usb6/6-1/6-1:1.1 -> 1 */
static int get_interface_number(char *path)
{
char *c;
c = strstr(path, vhci_driver->hc_device->bus_id);
if (!c)
return -1; /* hc exist? */
c++;
/* -> usb6/6-1/6-1:1.1 */
c = strchr(c, '/');
if (!c)
return -1; /* hc exist? */
c++;
/* -> 6-1/6-1:1.1 */
c = strchr(c, '/');
if (!c)
return -1; /* no interface path */
c++;
/* -> 6-1:1.1 */
c = strchr(c, ':');
if (!c)
return -1; /* no configuration? */
c++;
/* -> 1.1 */
c = strchr(c, '.');
if (!c)
return -1; /* no interface? */
c++;
/* -> 1 */
return atoi(c);
}
static struct sysfs_device *open_usb_interface(struct usb_device *udev, int i)
{
struct sysfs_device *suinf;
char busid[SYSFS_BUS_ID_SIZE];
snprintf(busid, SYSFS_BUS_ID_SIZE, "%s:%d.%d",
udev->busid, udev->bConfigurationValue, i);
suinf = sysfs_open_device("usb", busid);
if (!suinf)
err("sysfs_open_device %s", busid);
return suinf;
}
#define MAX_BUFF 100
static int record_connection(char *host, char *port, char *busid, int rhport)
{
int fd;
char path[PATH_MAX+1];
char buff[MAX_BUFF+1];
int ret;
mkdir(VHCI_STATE_PATH, 0700);
snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
if (fd < 0)
return -1;
snprintf(buff, MAX_BUFF, "%s %s %s\n",
host, port, busid);
ret = write(fd, buff, strlen(buff));
if (ret != (ssize_t) strlen(buff)) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int read_record(int rhport, char *host, char *port, char *busid)
{
FILE *file;
char path[PATH_MAX+1];
snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
file = fopen(path, "r");
if (!file) {
err("fopen");
return -1;
}
if (fscanf(file, "%s %s %s\n", host, port, busid) != 3) {
err("fscanf");
fclose(file);
return -1;
}
fclose(file);
return 0;
}
int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
{
char product_name[100];
char host[NI_MAXHOST] = "unknown host";
char serv[NI_MAXSERV] = "unknown port";
char remote_busid[SYSFS_BUS_ID_SIZE];
int ret;
if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED) {
info("Port %02d: <%s>", idev->port, usbip_status_string(idev->status));
return 0;
}
ret = read_record(idev->port, host, serv, remote_busid);
if (ret) {
err("read_record");
return -1;
}
info("Port %02d: <%s> at %s", idev->port,
usbip_status_string(idev->status), usbip_speed_string(idev->udev.speed));
usbip_names_get_product(product_name, sizeof(product_name),
idev->udev.idVendor, idev->udev.idProduct);
info(" %s", product_name); #include <getopt.h>
info("%10s -> usbip://%s:%s/%s (remote devid %08x (bus/dev %03d/%03d))",
idev->udev.busid, host, serv, remote_busid,
idev->devid,
idev->busnum, idev->devnum);
for (int i=0; i < idev->udev.bNumInterfaces; i++) {
/* show interface information */
struct sysfs_device *suinf;
suinf = open_usb_interface(&idev->udev, i);
if (!suinf)
continue;
info(" %6s used by %-17s", suinf->bus_id, suinf->driver_name);
sysfs_close_device(suinf);
/* show class device information */
struct usbip_class_device *cdev;
dlist_for_each_data(idev->cdev_list, cdev,
struct usbip_class_device) {
int ifnum = get_interface_number(cdev->dev_path);
if (ifnum == i) {
info(" %s", cdev->class_path);
}
}
}
return 0;
}
static int query_exported_devices(int sockfd)
{
int ret;
struct op_devlist_reply rep;
uint16_t code = OP_REP_DEVLIST;
bzero(&rep, sizeof(rep));
ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
if (ret < 0) {
err("send op_common");
return -1;
}
ret = usbip_recv_op_common(sockfd, &code);
if (ret < 0) {
err("recv op_common");
return -1;
}
ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep));
if (ret < 0) {
err("recv op_devlist");
return -1;
}
PACK_OP_DEVLIST_REPLY(0, &rep);
dbg("exportable %d devices", rep.ndev);
for (unsigned int i=0; i < rep.ndev; i++) {
char product_name[100];
char class_name[100];
struct usb_device udev;
bzero(&udev, sizeof(udev));
ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev));
if (ret < 0) {
err("recv usb_device[%d]", i);
return -1;
}
pack_usb_device(0, &udev);
usbip_names_get_product(product_name, sizeof(product_name),
udev.idVendor, udev.idProduct);
usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass,
udev.bDeviceSubClass, udev.bDeviceProtocol);
info("%8s: %s", udev.busid, product_name);
info("%8s: %s", " ", udev.path);
info("%8s: %s", " ", class_name);
for (int j=0; j < udev.bNumInterfaces; j++) {
struct usb_interface uinf;
ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf));
if (ret < 0) {
err("recv usb_interface[%d]", j);
return -1;
}
pack_usb_interface(0, &uinf);
usbip_names_get_class(class_name, sizeof(class_name), uinf.bInterfaceClass,
uinf.bInterfaceSubClass, uinf.bInterfaceProtocol);
info("%8s: %2d - %s", " ", j, class_name);
}
info(" ");
}
return rep.ndev;
}
static int import_device(int sockfd, struct usb_device *udev)
{
int ret;
int port;
ret = usbip_vhci_driver_open();
if (ret < 0) {
err("open vhci_driver");
return -1;
}
port = usbip_vhci_get_free_port();
if (port < 0) {
err("no free port");
usbip_vhci_driver_close();
return -1;
}
ret = usbip_vhci_attach_device(port, sockfd, udev->busnum, #include "usbip_common.h"
udev->devnum, udev->speed); #include "usbip.h"
if (ret < 0) {
err("import device");
usbip_vhci_driver_close();
return -1;
}
usbip_vhci_driver_close(); static int usbip_help(int argc, char *argv[]);
static int usbip_version(int argc, char *argv[]);
return port; static const char usbip_version_string[] = PACKAGE_STRING;
}
static const char usbip_usage_string[] =
"usbip [--debug] [version]\n"
" [help] <command> <args>\n";
static int query_import_device(int sockfd, char *busid) static void usbip_usage(void)
{ {
int ret; printf("usage: %s", usbip_usage_string);
struct op_import_request request;
struct op_import_reply reply;
uint16_t code = OP_REP_IMPORT;
bzero(&request, sizeof(request));
bzero(&reply, sizeof(reply));
/* send a request */
ret = usbip_send_op_common(sockfd, OP_REQ_IMPORT, 0);
if (ret < 0) {
err("send op_common");
return -1;
}
strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
PACK_OP_IMPORT_REQUEST(0, &request);
ret = usbip_send(sockfd, (void *) &request, sizeof(request));
if (ret < 0) {
err("send op_import_request");
return -1;
}
/* recieve a reply */
ret = usbip_recv_op_common(sockfd, &code);
if (ret < 0) {
err("recv op_common");
return -1;
}
ret = usbip_recv(sockfd, (void *) &reply, sizeof(reply));
if (ret < 0) {
err("recv op_import_reply");
return -1;
}
PACK_OP_IMPORT_REPLY(0, &reply);
/* check the reply */
if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
err("recv different busid %s", reply.udev.busid);
return -1;
}
/* import a device */
return import_device(sockfd, &reply.udev);
} }
static int attach_device(char *host, char *busid) struct command {
{ const char *name;
int sockfd; int (*fn)(int argc, char *argv[]);
int ret; const char *help;
int rhport; void (*usage)(void);
};
sockfd = tcp_connect(host, USBIP_PORT_STRING);
if (sockfd < 0) {
err("tcp connect");
return -1;
}
rhport = query_import_device(sockfd, busid);
if (rhport < 0) {
err("query");
return -1;
}
close(sockfd);
ret = record_connection(host, USBIP_PORT_STRING,
busid, rhport);
if (ret < 0) {
err("record connection");
return -1;
}
return 0; static const struct command cmds[] = {
} {
.name = "help",
.fn = usbip_help,
.help = NULL,
.usage = NULL
},
{
.name = "version",
.fn = usbip_version,
.help = NULL,
.usage = NULL
},
{
.name = "attach",
.fn = usbip_attach,
.help = "Attach a remote USB device",
.usage = usbip_attach_usage
},
{
.name = "detach",
.fn = usbip_detach,
.help = "Detach a remote USB device",
.usage = usbip_detach_usage
},
{
.name = "list",
.fn = usbip_list,
.help = "List exported or local USB devices",
.usage = usbip_list_usage
},
{
.name = "bind",
.fn = usbip_bind,
.help = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
.usage = usbip_bind_usage
},
{
.name = "unbind",
.fn = usbip_unbind,
.help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
.usage = usbip_unbind_usage
},
{ NULL, NULL, NULL, NULL }
};
static int detach_port(char *port) static int usbip_help(int argc, char *argv[])
{ {
int ret; const struct command *cmd;
uint8_t portnum; int i;
int ret = 0;
for (unsigned int i=0; i < strlen(port); i++) if (argc > 1 && argv++) {
if (!isdigit(port[i])) { for (i = 0; cmds[i].name != NULL; i++)
err("invalid port %s", port); if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
return -1; cmds[i].usage();
goto done;
} }
ret = -1;
/* check max port */
portnum = atoi(port);
ret = usbip_vhci_driver_open();
if (ret < 0) {
err("open vhci_driver");
return -1;
} }
ret = usbip_vhci_detach_device(portnum); usbip_usage();
if (ret < 0) printf("\n");
return -1; for (cmd = cmds; cmd->name != NULL; cmd++)
if (cmd->help != NULL)
usbip_vhci_driver_close(); printf(" %-10s %s\n", cmd->name, cmd->help);
printf("\n");
done:
return ret; return ret;
} }
static int show_exported_devices(char *host) static int usbip_version(int argc, char *argv[])
{
int ret;
int sockfd;
sockfd = tcp_connect(host, USBIP_PORT_STRING);
if (sockfd < 0) {
err("- %s failed", host);
return -1;
}
info("- %s", host);
ret = query_exported_devices(sockfd);
if (ret < 0) {
err("query");
return -1;
}
close(sockfd);
return 0;
}
static int attach_exported_devices(char *host, int sockfd)
{
int ret;
struct op_devlist_reply rep;
uint16_t code = OP_REP_DEVLIST;
bzero(&rep, sizeof(rep));
ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
if(ret < 0) {
err("send op_common");
return -1;
}
ret = usbip_recv_op_common(sockfd, &code);
if(ret < 0) {
err("recv op_common");
return -1;
}
ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep));
if(ret < 0) {
err("recv op_devlist");
return -1;
}
PACK_OP_DEVLIST_REPLY(0, &rep);
dbg("exportable %d devices", rep.ndev);
for(unsigned int i=0; i < rep.ndev; i++) {
char product_name[100];
char class_name[100];
struct usb_device udev;
bzero(&udev, sizeof(udev));
ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev));
if(ret < 0) {
err("recv usb_device[%d]", i);
return -1;
}
pack_usb_device(0, &udev);
usbip_names_get_product(product_name, sizeof(product_name),
udev.idVendor, udev.idProduct);
usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass,
udev.bDeviceSubClass, udev.bDeviceProtocol);
dbg("Attaching usb port %s from host %s on usbip, with deviceid: %s", udev.busid, host, product_name);
for (int j=0; j < udev.bNumInterfaces; j++) {
struct usb_interface uinf;
ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf));
if (ret < 0) {
err("recv usb_interface[%d]", j);
return -1;
}
pack_usb_interface(0, &uinf);
usbip_names_get_class(class_name, sizeof(class_name), uinf.bInterfaceClass,
uinf.bInterfaceSubClass, uinf.bInterfaceProtocol);
dbg("interface %2d - %s", j, class_name);
}
attach_device(host, udev.busid);
}
return rep.ndev;
}
static int attach_devices_all(char *host)
{ {
int ret; (void) argc;
int sockfd; (void) argv;
sockfd = tcp_connect(host, USBIP_PORT_STRING); printf("%s\n", usbip_version_string);
if(sockfd < 0) {
err("- %s failed", host);
return -1;
}
info("- %s", host);
ret = attach_exported_devices(host, sockfd);
if(ret < 0) {
err("query");
return -1;
}
close(sockfd);
return 0; return 0;
} }
static int run_command(const struct command *cmd, int argc, char *argv[])
const char help_message[] = "\
Usage: usbip [options] \n\
-a, --attach [host] [bus_id] \n\
Attach a remote USB device. \n\
\n\
-x, --attachall [host] \n\
Attach all remote USB devices on the specific host. \n\
\n\
-d, --detach [ports] \n\
Detach an imported USB device. \n\
\n\
-l, --list [hosts] \n\
List exported USB devices. \n\
\n\
-p, --port \n\
List virtual USB port status. \n\
\n\
-D, --debug \n\
Print debugging information. \n\
\n\
-v, --version \n\
Show version. \n\
\n\
-h, --help \n\
Print this help. \n";
static void show_help(void)
{ {
printf("%s", help_message); dbg("running command: `%s'\n", cmd->name);
return cmd->fn(argc, argv);
} }
static int show_port_status(void)
{
int ret;
struct usbip_imported_device *idev;
ret = usbip_vhci_driver_open();
if (ret < 0)
return ret;
for (int i = 0; i < vhci_driver->nports; i++) {
idev = &vhci_driver->idev[i];
if (usbip_vhci_imported_device_dump(idev) < 0)
ret = -1;
}
usbip_vhci_driver_close();
return ret;
}
#define _GNU_SOURCE
#include <getopt.h>
static const struct option longopts[] = {
{"attach", no_argument, NULL, 'a'},
{"attachall", no_argument, NULL, 'x'},
{"detach", no_argument, NULL, 'd'},
{"port", no_argument, NULL, 'p'},
{"list", no_argument, NULL, 'l'},
{"version", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 'h'},
{"debug", no_argument, NULL, 'D'},
{"syslog", no_argument, NULL, 'S'},
{NULL, 0, NULL, 0}
};
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret; static const struct option opts[] = {
{ "debug", no_argument, NULL, 'd' },
enum { { NULL, 0, NULL, 0 }
cmd_attach = 1, };
cmd_attachall, char *cmd;
cmd_detach, int opt;
cmd_port, int i, rc = -1;
cmd_list,
cmd_help,
cmd_version
} cmd = 0;
usbip_use_stderr = 1;
if (geteuid() != 0)
g_warning("running non-root?");
ret = usbip_names_init(USBIDS_FILE);
if (ret)
notice("failed to open %s", USBIDS_FILE);
opterr = 0;
for (;;) { for (;;) {
int c; opt = getopt_long(argc, argv, "+d", opts, NULL);
int index = 0;
c = getopt_long(argc, argv, "adplvhDSx", longopts, &index); if (opt == -1)
if (c == -1)
break; break;
switch(c) { switch (opt) {
case 'a':
if (!cmd)
cmd = cmd_attach;
else
cmd = cmd_help;
break;
case 'd': case 'd':
if (!cmd)
cmd = cmd_detach;
else
cmd = cmd_help;
break;
case 'p':
if (!cmd)
cmd = cmd_port;
else cmd = cmd_help;
break;
case 'l':
if (!cmd)
cmd = cmd_list;
else
cmd = cmd_help;
break;
case 'v':
if (!cmd)
cmd = cmd_version;
else
cmd = cmd_help;
break;
case 'x':
if(!cmd)
cmd = cmd_attachall;
else
cmd = cmd_help;
break;
case 'h':
cmd = cmd_help;
break;
case 'D':
usbip_use_debug = 1; usbip_use_debug = 1;
usbip_use_stderr = 1;
break; break;
case 'S':
usbip_use_syslog = 1;
break;
case '?':
break;
default: default:
err("getopt"); goto err_out;
} }
} }
ret = 0; cmd = argv[optind];
switch(cmd) { if (cmd) {
case cmd_attach: for (i = 0; cmds[i].name != NULL; i++)
if (optind == argc - 2) if (!strcmp(cmds[i].name, cmd)) {
ret = attach_device(argv[optind], argv[optind+1]); argc -= optind;
else argv += optind;
show_help(); optind = 0;
break; rc = run_command(&cmds[i], argc, argv);
case cmd_detach: goto out;
while (optind < argc) }
ret = detach_port(argv[optind++]);
break;
case cmd_port:
ret = show_port_status();
break;
case cmd_list:
while (optind < argc)
ret = show_exported_devices(argv[optind++]);
break;
case cmd_attachall:
while(optind < argc)
ret = attach_devices_all(argv[optind++]);
break;
case cmd_version:
printf("%s\n", version);
break;
case cmd_help:
show_help();
break;
default:
show_help();
} }
err_out:
usbip_names_free(); usbip_usage();
out:
exit((ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE); return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
} }
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_H
#define __USBIP_H
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
/* usbip commands */
int usbip_attach(int argc, char *argv[]);
int usbip_detach(int argc, char *argv[]);
int usbip_list(int argc, char *argv[]);
int usbip_bind(int argc, char *argv[]);
int usbip_unbind(int argc, char *argv[]);
void usbip_attach_usage(void);
void usbip_detach_usage(void);
void usbip_list_usage(void);
void usbip_bind_usage(void);
void usbip_unbind_usage(void);
#endif /* __USBIP_H */
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h>
#include <sysfs/libsysfs.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.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_attach_usage_string[] =
"usbip attach <args>\n"
" -h, --host=<host> The machine with exported USB devices\n"
" -b, --busid=<busid> Busid of the device on <host>\n";
void usbip_attach_usage(void)
{
printf("usage: %s", usbip_attach_usage_string);
}
#define MAX_BUFF 100
static int record_connection(char *host, char *port, char *busid, int rhport)
{
int fd;
char path[PATH_MAX+1];
char buff[MAX_BUFF+1];
int ret;
mkdir(VHCI_STATE_PATH, 0700);
snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
if (fd < 0)
return -1;
snprintf(buff, MAX_BUFF, "%s %s %s\n",
host, port, busid);
ret = write(fd, buff, strlen(buff));
if (ret != (ssize_t) strlen(buff)) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int import_device(int sockfd, struct usb_device *udev)
{
int rc;
int port;
rc = usbip_vhci_driver_open();
if (rc < 0) {
err("open vhci_driver");
return -1;
}
port = usbip_vhci_get_free_port();
if (port < 0) {
err("no free port");
usbip_vhci_driver_close();
return -1;
}
rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
udev->devnum, udev->speed);
if (rc < 0) {
err("import device");
usbip_vhci_driver_close();
return -1;
}
usbip_vhci_driver_close();
return port;
}
static int query_import_device(int sockfd, char *busid)
{
int rc;
struct op_import_request request;
struct op_import_reply reply;
uint16_t code = OP_REP_IMPORT;
memset(&request, 0, sizeof(request));
memset(&reply, 0, sizeof(reply));
/* send a request */
rc = usbip_send_op_common(sockfd, OP_REQ_IMPORT, 0);
if (rc < 0) {
err("send op_common");
return -1;
}
strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
PACK_OP_IMPORT_REQUEST(0, &request);
rc = usbip_send(sockfd, (void *) &request, sizeof(request));
if (rc < 0) {
err("send op_import_request");
return -1;
}
/* recieve a reply */
rc = usbip_recv_op_common(sockfd, &code);
if (rc < 0) {
err("recv op_common");
return -1;
}
rc = usbip_recv(sockfd, (void *) &reply, sizeof(reply));
if (rc < 0) {
err("recv op_import_reply");
return -1;
}
PACK_OP_IMPORT_REPLY(0, &reply);
/* check the reply */
if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
err("recv different busid %s", reply.udev.busid);
return -1;
}
/* import a device */
return import_device(sockfd, &reply.udev);
}
static int attach_device(char *host, char *busid)
{
int sockfd;
int rc;
int rhport;
sockfd = usbip_net_tcp_connect(host, USBIP_PORT_STRING);
if (sockfd < 0) {
err("tcp connect");
return -1;
}
rhport = query_import_device(sockfd, busid);
if (rhport < 0) {
err("query");
return -1;
}
close(sockfd);
rc = record_connection(host, USBIP_PORT_STRING, busid, rhport);
if (rc < 0) {
err("record connection");
return -1;
}
return 0;
}
int usbip_attach(int argc, char *argv[])
{
static const struct option opts[] = {
{ "host", required_argument, NULL, 'h' },
{ "busid", required_argument, NULL, 'b' },
{ NULL, 0, NULL, 0 }
};
char *host = NULL;
char *busid = NULL;
int opt;
int ret = -1;
for (;;) {
opt = getopt_long(argc, argv, "h:b:", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'h':
host = optarg;
break;
case 'b':
busid = optarg;
break;
default:
goto err_out;
}
}
if (!host || !busid)
goto err_out;
ret = attach_device(host, busid);
goto out;
err_out:
usbip_attach_usage();
out:
return ret;
}
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sysfs/libsysfs.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include "usbip_common.h"
#include "utils.h"
#include "usbip.h"
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);
}
static const char unbind_path_format[] = "/sys/bus/usb/devices/%s/driver/unbind";
/* buggy driver may cause dead lock */
static int unbind_interface_busid(char *busid)
{
char unbind_path[SYSFS_PATH_MAX];
int fd;
int ret;
snprintf(unbind_path, sizeof(unbind_path), unbind_path_format, busid);
fd = open(unbind_path, O_WRONLY);
if (fd < 0) {
dbg("opening unbind_path failed: %d", fd);
return -1;
}
ret = write(fd, busid, strnlen(busid, BUS_ID_SIZE));
if (ret < 0) {
dbg("write to unbind_path failed: %d", ret);
close(fd);
return -1;
}
close(fd);
return 0;
}
static int unbind_interface(char *busid, int configvalue, int interface)
{
char inf_busid[BUS_ID_SIZE];
dbg("unbinding interface");
snprintf(inf_busid, BUS_ID_SIZE, "%s:%d.%d", busid, configvalue, interface);
return unbind_interface_busid(inf_busid);
}
static int unbind(char *busid)
{
int configvalue = 0;
int ninterface = 0;
int devclass = 0;
int i;
int failed = 0;
configvalue = read_bConfigurationValue(busid);
ninterface = read_bNumInterfaces(busid);
devclass = read_bDeviceClass(busid);
if (configvalue < 0 || ninterface < 0 || devclass < 0) {
dbg("read config and ninf value, removed?");
return -1;
}
if (devclass == 0x09) {
dbg("skip unbinding of hub");
return -1;
}
for (i = 0; i < ninterface; i++) {
char driver[PATH_MAX];
int ret;
memset(&driver, 0, sizeof(driver));
getdriver(busid, configvalue, i, driver, PATH_MAX-1);
dbg(" %s:%d.%d -> %s ", busid, configvalue, i, driver);
if (!strncmp("none", driver, PATH_MAX))
continue; /* unbound interface */
#if 0
if (!strncmp("usbip", driver, PATH_MAX))
continue; /* already bound to usbip */
#endif
/* unbinding */
ret = unbind_interface(busid, configvalue, i);
if (ret < 0) {
dbg("unbind driver at %s:%d.%d failed",
busid, configvalue, i);
failed = 1;
}
}
if (failed)
return -1;
else
return 0;
}
static const char bind_path_format[] = "/sys/bus/usb/drivers/%s/bind";
static int bind_interface_busid(char *busid, char *driver)
{
char bind_path[PATH_MAX];
int fd;
int ret;
snprintf(bind_path, sizeof(bind_path), bind_path_format, driver);
fd = open(bind_path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, busid, strnlen(busid, BUS_ID_SIZE));
if (ret < 0) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int bind_interface(char *busid, int configvalue, int interface, char *driver)
{
char inf_busid[BUS_ID_SIZE];
snprintf(inf_busid, BUS_ID_SIZE, "%s:%d.%d", busid, configvalue, interface);
return bind_interface_busid(inf_busid, driver);
}
/* call at unbound state */
static int bind_to_usbip(char *busid)
{
int configvalue = 0;
int ninterface = 0;
int i;
int failed = 0;
configvalue = read_bConfigurationValue(busid);
ninterface = read_bNumInterfaces(busid);
if (configvalue < 0 || ninterface < 0) {
dbg("read config and ninf value, removed?");
return -1;
}
for (i = 0; i < ninterface; i++) {
int ret;
ret = bind_interface(busid, configvalue, i,
USBIP_HOST_DRV_NAME);
if (ret < 0) {
dbg("bind usbip at %s:%d.%d, failed",
busid, configvalue, i);
failed = 1;
/* need to contine binding at other interfaces */
}
}
if (failed)
return -1;
else
return 0;
}
static int use_device_by_usbip(char *busid)
{
int ret;
ret = unbind(busid);
if (ret < 0) {
dbg("unbind drivers of %s, failed", busid);
return -1;
}
ret = modify_match_busid(busid, 1);
if (ret < 0) {
dbg("add %s to match_busid, failed", busid);
return -1;
}
ret = bind_to_usbip(busid);
if (ret < 0) {
dbg("bind usbip to %s, failed", busid);
modify_match_busid(busid, 0);
return -1;
}
dbg("bind %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 = use_device_by_usbip(optarg);
goto out;
default:
goto err_out;
}
}
err_out:
usbip_bind_usage();
out:
return ret;
}
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sysfs/libsysfs.h>
#include <ctype.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;
uint8_t portnum;
for (unsigned int i=0; i < strlen(port); i++)
if (!isdigit(port[i])) {
err("invalid port %s", port);
return -1;
}
/* check max port */
portnum = atoi(port);
ret = usbip_vhci_driver_open();
if (ret < 0) {
err("open vhci_driver");
return -1;
}
ret = usbip_vhci_detach_device(portnum);
if (ret < 0)
return -1;
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;
}
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sysfs/libsysfs.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <getopt.h>
#include <netdb.h>
#include <regex.h>
#include <unistd.h>
#include "usbip_common.h"
#include "usbip_network.h"
#include "utils.h"
#include "usbip.h"
static const char usbip_list_usage_string[] =
"usbip list [-p|--parsable] <args>\n"
" -p, --parsable Parsable list format\n"
" -r, --remote=<host> List the exported USB devices on <host>\n"
" -l, --local List the local USB devices\n";
void usbip_list_usage(void)
{
printf("usage: %s", usbip_list_usage_string);
}
static int query_exported_devices(int sockfd)
{
int ret;
struct op_devlist_reply rep;
uint16_t code = OP_REP_DEVLIST;
memset(&rep, 0, sizeof(rep));
ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
if (ret < 0) {
err("send op_common");
return -1;
}
ret = usbip_recv_op_common(sockfd, &code);
if (ret < 0) {
err("recv op_common");
return -1;
}
ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep));
if (ret < 0) {
err("recv op_devlist");
return -1;
}
PACK_OP_DEVLIST_REPLY(0, &rep);
dbg("exportable %d devices", rep.ndev);
for (unsigned int i=0; i < rep.ndev; i++) {
char product_name[100];
char class_name[100];
struct usb_device udev;
memset(&udev, 0, sizeof(udev));
ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev));
if (ret < 0) {
err("recv usb_device[%d]", i);
return -1;
}
pack_usb_device(0, &udev);
usbip_names_get_product(product_name, sizeof(product_name),
udev.idVendor, udev.idProduct);
usbip_names_get_class(class_name, sizeof(class_name),
udev.bDeviceClass, udev.bDeviceSubClass,
udev.bDeviceProtocol);
printf("%8s: %s\n", udev.busid, product_name);
printf("%8s: %s\n", " ", udev.path);
printf("%8s: %s\n", " ", class_name);
for (int j=0; j < udev.bNumInterfaces; j++) {
struct usb_interface uinf;
ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf));
if (ret < 0) {
err("recv usb_interface[%d]", j);
return -1;
}
pack_usb_interface(0, &uinf);
usbip_names_get_class(class_name, sizeof(class_name),
uinf.bInterfaceClass,
uinf.bInterfaceSubClass,
uinf.bInterfaceProtocol);
printf("%8s: %2d - %s\n", " ", j, class_name);
}
printf("\n");
}
return rep.ndev;
}
static int show_exported_devices(char *host)
{
int ret;
int sockfd;
sockfd = usbip_net_tcp_connect(host, USBIP_PORT_STRING);
if (sockfd < 0) {
err("unable to connect to %s port %s: %s\n", host,
USBIP_PORT_STRING, gai_strerror(sockfd));
return -1;
}
dbg("connected to %s port %s\n", host, USBIP_PORT_STRING);
printf("- %s\n", host);
ret = query_exported_devices(sockfd);
if (ret < 0) {
err("query");
return -1;
}
close(sockfd);
return 0;
}
static int is_usb_device(char *busid)
{
int ret;
regex_t regex;
regmatch_t pmatch[1];
ret = regcomp(&regex, "^[0-9]+-[0-9]+(\\.[0-9]+)*$", REG_NOSUB|REG_EXTENDED);
if (ret < 0)
err("regcomp: %s\n", strerror(errno));
ret = regexec(&regex, busid, 0, pmatch, 0);
if (ret)
return 0; /* not matched */
return 1;
}
static int show_devices(void)
{
DIR *dir;
dir = opendir("/sys/bus/usb/devices/");
if (!dir)
err("opendir: %s", strerror(errno));
printf("List USB devices\n");
for (;;) {
struct dirent *dirent;
char *busid;
dirent = readdir(dir);
if (!dirent)
break;
busid = dirent->d_name;
if (is_usb_device(busid)) {
char name[100] = {'\0'};
char driver[100] = {'\0'};
int conf, ninf = 0;
int i;
conf = read_bConfigurationValue(busid);
ninf = read_bNumInterfaces(busid);
getdevicename(busid, name, sizeof(name));
printf(" - busid %s (%s)\n", busid, name);
for (i = 0; i < ninf; i++) {
getdriver(busid, conf, i, driver,
sizeof(driver));
printf(" %s:%d.%d -> %s\n", busid, conf,
i, driver);
}
printf("\n");
}
}
closedir(dir);
return 0;
}
static int show_devices2(void)
{
DIR *dir;
dir = opendir("/sys/bus/usb/devices/");
if (!dir)
err("opendir: %s", strerror(errno));
for (;;) {
struct dirent *dirent;
char *busid;
dirent = readdir(dir);
if (!dirent)
break;
busid = dirent->d_name;
if (is_usb_device(busid)) {
char name[100] = {'\0'};
char driver[100] = {'\0'};
int conf, ninf = 0;
int i;
conf = read_bConfigurationValue(busid);
ninf = read_bNumInterfaces(busid);
getdevicename(busid, name, sizeof(name));
printf("busid=%s#usbid=%s#", busid, name);
for (i = 0; i < ninf; i++) {
getdriver(busid, conf, i, driver, sizeof(driver));
printf("%s:%d.%d=%s#", busid, conf, i, driver);
}
printf("\n");
}
}
closedir(dir);
return 0;
}
int usbip_list(int argc, char *argv[])
{
static const struct option opts[] = {
{ "parsable", no_argument, NULL, 'p' },
{ "remote", required_argument, NULL, 'r' },
{ "local", no_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 }
};
bool is_parsable = false;
int opt;
int ret = -1;
if (usbip_names_init(USBIDS_FILE))
err("failed to open %s\n", USBIDS_FILE);
for (;;) {
opt = getopt_long(argc, argv, "pr:l", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'p':
is_parsable = true;
break;
case 'r':
ret = show_exported_devices(optarg);
goto out;
case 'l':
if (is_parsable)
ret = show_devices2();
else
ret = show_devices();
goto out;
default:
goto err_out;
}
}
err_out:
usbip_list_usage();
out:
usbip_names_free();
return ret;
}
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <getopt.h>
#include <unistd.h>
#include "usbip_common.h"
#include "utils.h"
#include "usbip.h"
static const char usbip_unbind_usage_string[] =
"usbip unbind <args>\n"
" -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from "
"device on <busid>\n";
void usbip_unbind_usage(void)
{
printf("usage: %s", usbip_unbind_usage_string);
}
static int use_device_by_other(char *busid)
{
int rc;
int config;
/* read and write the same config value to kick probing */
config = read_bConfigurationValue(busid);
if (config < 0) {
dbg("read bConfigurationValue of %s, failed", busid);
return -1;
}
rc = modify_match_busid(busid, 0);
if (rc < 0) {
dbg("del %s to match_busid, failed", busid);
return -1;
}
rc = write_bConfigurationValue(busid, config);
if (rc < 0) {
dbg("read bConfigurationValue of %s, failed", busid);
return -1;
}
info("bind %s to other drivers than usbip, complete!", busid);
return 0;
}
int usbip_unbind(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 = use_device_by_other(optarg);
goto out;
default:
goto err_out;
}
}
err_out:
usbip_unbind_usage();
out:
return ret;
}
...@@ -3,8 +3,61 @@ ...@@ -3,8 +3,61 @@
* Copyright (C) 2005-2007 Takahiro Hirofuchi * Copyright (C) 2005-2007 Takahiro Hirofuchi
*/ */
#include <sysfs/libsysfs.h>
#include <fcntl.h>
#include <libgen.h>
#include <string.h>
#include <unistd.h>
#include "usbip_common.h"
#include "utils.h" #include "utils.h"
int modify_match_busid(char *busid, int add)
{
int fd;
int ret;
char buff[BUS_ID_SIZE + 4];
char sysfs_mntpath[SYSFS_PATH_MAX];
char match_busid_path[SYSFS_PATH_MAX];
ret = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (ret < 0) {
err("sysfs must be mounted");
return -1;
}
snprintf(match_busid_path, sizeof(match_busid_path),
"%s/%s/usb/%s/%s/match_busid", sysfs_mntpath, SYSFS_BUS_NAME,
SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME);
/* BUS_IS_SIZE includes NULL termination? */
if (strnlen(busid, BUS_ID_SIZE) > BUS_ID_SIZE - 1) {
dbg("busid is too long");
return -1;
}
fd = open(match_busid_path, O_WRONLY);
if (fd < 0)
return -1;
if (add)
snprintf(buff, BUS_ID_SIZE + 4, "add %s", busid);
else
snprintf(buff, BUS_ID_SIZE + 4, "del %s", busid);
dbg("write \"%s\" to %s", buff, match_busid_path);
ret = write(fd, buff, sizeof(buff));
if (ret < 0) {
close(fd);
return -1;
}
close(fd);
return 0;
}
int read_integer(char *path) int read_integer(char *path)
{ {
char buff[100]; char buff[100];
...@@ -36,7 +89,7 @@ int read_string(char *path, char *string, size_t len) ...@@ -36,7 +89,7 @@ int read_string(char *path, char *string, size_t len)
int ret = 0; int ret = 0;
char *p; char *p;
bzero(string, len); memset(string, 0, len);
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd < 0) { if (fd < 0) {
...@@ -122,15 +175,16 @@ int getdriver(char *busid, int conf, int infnum, char *driver, size_t len) ...@@ -122,15 +175,16 @@ int getdriver(char *busid, int conf, int infnum, char *driver, size_t len)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
char linkto[PATH_MAX]; char linkto[PATH_MAX];
const char none[] = "none";
int ret; int ret;
snprintf(path, PATH_MAX, "/sys/bus/usb/devices/%s:%d.%d/driver", busid, conf, infnum); snprintf(path, PATH_MAX, "/sys/bus/usb/devices/%s:%d.%d/driver", busid, conf, infnum);
/* readlink does not add NULL */ /* readlink does not add NULL */
bzero(linkto, sizeof(linkto)); memset(linkto, 0, sizeof(linkto));
ret = readlink(path, linkto, sizeof(linkto)-1); ret = readlink(path, linkto, sizeof(linkto)-1);
if (ret < 0) { if (ret < 0) {
strncpy(driver, "none", len); strncpy(driver, none, len);
return -1; return -1;
} else { } else {
strncpy(driver, basename(linkto), len); strncpy(driver, basename(linkto), len);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
/* Be sync to kernel header */ /* Be sync to kernel header */
#define BUS_ID_SIZE 20 #define BUS_ID_SIZE 20
int modify_match_busid(char *busid, int add);
int read_string(char *path, char *, size_t len); int read_string(char *path, char *, size_t len);
int read_integer(char *path); int read_integer(char *path);
int getdevicename(char *busid, char *name, size_t len); int getdevicename(char *busid, char *name, size_t len);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment