blob: 01a562866b5d5fc008312e9606de4b99a2d5e6a3 [file] [log] [blame]
/*
*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#ifdef HAVE_CONFIG_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 <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);
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 class_device *cdev;
dlist_for_each_data(idev->cdev_list, cdev, struct class_device) {
int ifnum = get_interface_number(cdev->devpath);
if (ifnum == i) {
info(" %s", cdev->clspath);
}
}
}
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,
udev->devnum, udev->speed);
if (ret < 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 ret;
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)
{
int sockfd;
int ret;
int rhport;
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 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;
}
static int show_exported_devices(char *host)
{
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;
int sockfd;
sockfd = tcp_connect(host, USBIP_PORT_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;
}
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);
}
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 ret;
enum {
cmd_attach = 1,
cmd_attachall,
cmd_detach,
cmd_port,
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);
for (;;) {
int c;
int index = 0;
c = getopt_long(argc, argv, "adplvhDSx", longopts, &index);
if (c == -1)
break;
switch(c) {
case 'a':
if (!cmd)
cmd = cmd_attach;
else
cmd = cmd_help;
break;
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;
break;
case 'S':
usbip_use_syslog = 1;
break;
case '?':
break;
default:
err("getopt");
}
}
ret = 0;
switch(cmd) {
case cmd_attach:
if (optind == argc - 2)
ret = attach_device(argv[optind], argv[optind+1]);
else
show_help();
break;
case cmd_detach:
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();
}
usbip_names_free();
exit((ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}