* [Qemu-devel] [PATCH] USB over network
@ 2008-10-06 11:43 Gal Hammer
2008-10-06 12:09 ` Paul Brook
` (2 more replies)
0 siblings, 3 replies; 13+ messages in thread
From: Gal Hammer @ 2008-10-06 11:43 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1.1: Type: text/plain, Size: 231 bytes --]
Hi,
Attached is a preliminary patch which add QEmu the ability to use local
USB devices over network. It should work with DOK devices and might work
with web cameras.
Thanks,
Gal.
[-- Attachment #1.2: Type: text/html, Size: 2107 bytes --]
[-- Attachment #2: usb-over-ip.patch --]
[-- Type: application/octet-stream, Size: 58722 bytes --]
diff --git a/Makefile b/Makefile
index 35061a4..ff8611f 100644
--- a/Makefile
+++ b/Makefile
@@ -78,7 +78,7 @@ OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
OBJS+=tmp105.o lm832x.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=scsi-generic.o
-OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
+OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-remote.o
OBJS+=usb-serial.o usb-net.o
OBJS+=sd.o ssi-sd.o
OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
diff --git a/hw/usb.h b/hw/usb.h
index 4204808..8e2c520 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -242,6 +242,7 @@ USBDevice *usb_hub_init(int nb_ports);
USBDevice *usb_host_device_open(const char *devname);
int usb_host_device_close(const char *devname);
void usb_host_info(void);
+int usb_remote_start(const char *host_str);
/* usb-hid.c */
USBDevice *usb_mouse_init(void);
diff --git a/qemu-usbd.c b/qemu-usbd.c
new file mode 100644
index 0000000..55bc334
--- /dev/null
+++ b/qemu-usbd.c
@@ -0,0 +1,1059 @@
+/*
+* Linux remote USB redirector
+*
+* Copyright (c) 2003-2008 Fabrice Bellard
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/usb_ch9.h>
+
+typedef struct NICInfo NICInfo;
+typedef struct HCIInfo HCIInfo;
+typedef struct musb_s musb_s;
+typedef struct IRQState *qemu_irq;
+
+#include "hw/usb.h"
+#include "usb-remote.h"
+
+#define USBDEVFS_PATH "/proc/bus/usb"
+#define PRODUCT_NAME_SZ 32
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+#define socket_error() errno
+#define closesocket(s) close(s)
+
+typedef struct USBDeviceX {
+ int fd;
+ int sock;
+
+ char devname[PRODUCT_NAME_SZ];
+ uint8_t descr[1024];
+ int descr_len;
+ int closing;
+
+ /* Host side address */
+ int bus_num;
+ int addr;
+
+ /* Network buffer */
+ char *tail_ptr;
+ char buffer[0x1000];
+
+ struct USBDeviceX *next;
+
+} USBDeviceX;
+
+void *qemu_mallocz(size_t size)
+{
+ void *ptr;
+ ptr = malloc(size);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void qemu_free(void *ptr)
+{
+ free(ptr);
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+static socket_set_nonblock(int s)
+{
+ int f;
+ f = fcntl(s, F_GETFL);
+ fcntl(s, F_SETFL, f | O_NONBLOCK);
+}
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+ int vendor_id, int product_id,
+ const char *product_name, int speed);
+
+static int usb_host_find_device(int *pbus_num, int *paddr,
+ char *product_name, int product_name_size,
+ const char *devname);
+
+static USBDeviceX *hostdev_list;
+
+static void hostdev_link(USBDeviceX *dev)
+{
+ dev->next = hostdev_list;
+ hostdev_list = dev;
+}
+
+static void hostdev_unlink(USBDeviceX *dev)
+{
+ USBDeviceX *pdev = hostdev_list;
+ USBDeviceX **prev = &hostdev_list;
+
+ while (pdev) {
+ if (pdev == dev) {
+ *prev = dev->next;
+ return;
+ }
+
+ prev = &pdev->next;
+ pdev = pdev->next;
+ }
+}
+
+static USBDeviceX *hostdev_find(int bus_num, int addr)
+{
+ USBDeviceX *s = hostdev_list;
+ while (s) {
+ if (s->bus_num == bus_num && s->addr == addr)
+ return s;
+ s = s->next;
+ }
+ return NULL;
+}
+
+/*
+* Async URB state.
+* We always allocate one isoc descriptor even for bulk transfers
+* to simplify allocation and casts.
+*/
+typedef struct AsyncURB
+{
+ struct usbdevfs_urb urb;
+ struct usbdevfs_iso_packet_desc isocpd;
+
+ USBPacket *packet;
+ USBDeviceX *dev;
+} AsyncURB;
+
+static AsyncURB *async_alloc(void)
+{
+ return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+}
+
+static void async_free(AsyncURB *aurb)
+{
+ qemu_free(aurb);
+}
+
+static void async_complete(USBDeviceX *s)
+{
+ UNRB_SUBMIT_URB nurb;
+ AsyncURB *aurb;
+ int ret;
+
+ while (1) {
+
+ ret = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ if (errno == ENODEV && !s->closing) {
+ printf("husb: device %d.%d disconnected\n", s->bus_num, s->addr);
+ return;
+ }
+
+ dprintf("husb: async. reap urb failed errno %d\n", errno);
+ return;
+ }
+
+ dprintf("husb: async completed. aurb %p status %d alen %d\n",
+ aurb, aurb->urb.status, aurb->urb.actual_length);
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_SUBMIT_URB;
+ nurb.header.size = sizeof(nurb) + aurb->urb.buffer_length;
+
+ nurb.urb.opaque = (uint64_t)(aurb->packet);
+
+ nurb.urb.type = aurb->urb.type;
+ nurb.urb.status = aurb->urb.status;
+ nurb.urb.length = aurb->urb.actual_length;
+
+ ret = send(s->sock, &nurb, sizeof(nurb), 0);
+ if (ret > 0) {
+ ret = send(s->sock, aurb->urb.buffer, aurb->urb.buffer_length, 0);
+ }
+
+ qemu_free(aurb->urb.buffer);
+ async_free(aurb);
+ }
+}
+
+static void async_cancel(USBPacket *unused, void *opaque)
+{
+ AsyncURB *aurb = opaque;
+ USBDeviceX *s = aurb->dev;
+
+ dprintf("husb: async cancel. aurb %p\n", aurb);
+
+ /* Mark it as dead (see async_complete above) */
+ aurb->packet = NULL;
+
+ int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+ if (r < 0) {
+ dprintf("husb: async. discard urb failed errno %d\n", errno);
+ }
+}
+
+static void usb_host_handle_destroy(USBDeviceX *s)
+{
+ s->closing = 1;
+
+ hostdev_unlink(s);
+
+ async_complete(s);
+
+ if (s->fd >= 0)
+ close(s->fd);
+
+ qemu_free(s);
+}
+
+static int ctrl_error(void)
+{
+ if (errno == ETIMEDOUT)
+ return USB_RET_NAK;
+ else
+ return USB_RET_STALL;
+}
+
+static int usb_host_set_config(USBDeviceX *s, int config)
+{
+ int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+
+ dprintf("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+
+ if (ret < 0)
+ return ctrl_error();
+
+ return 0;
+}
+
+static int usb_host_set_interface(USBDeviceX *s, int iface, int alt)
+{
+ struct usbdevfs_setinterface si;
+ int ret;
+
+ si.interface = iface;
+ si.altsetting = alt;
+ ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+
+ dprintf("husb: ctrl set iface %d altset %d ret %d errno %d\n",
+ iface, alt, ret, errno);
+
+ if (ret < 0)
+ return ctrl_error();
+
+ return 0;
+}
+
+static USBDeviceX *usbd_host_device_open_addr(int bus_num, int addr, const char *prod_name)
+{
+ USBDeviceX *dev = NULL;
+ int fd = -1, sock = -1;
+ unsigned long opt = 1;
+ char buf[1024];
+
+ dev = qemu_mallocz(sizeof(USBDeviceX));
+ if (!dev)
+ goto fail;
+
+ dev->bus_num = bus_num;
+ dev->addr = addr;
+
+ printf("husb: open device %d.%d\n", bus_num, addr);
+
+ snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
+ bus_num, addr);
+ fd = open(buf, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror(buf);
+ goto fail;
+ }
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ goto fail;
+ }
+
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt));
+
+ /* read the device description */
+ dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
+ if (dev->descr_len <= 0) {
+ perror("husb: reading device data failed");
+ goto fail;
+ }
+
+#ifdef DEBUG
+ {
+ int x;
+ printf("=== begin dumping device descriptor data ===\n");
+ for (x = 0; x < dev->descr_len; x++)
+ printf("%02x ", dev->descr[x]);
+ printf("\n=== end dumping device descriptor data ===\n");
+ }
+#endif
+
+ dev->fd = fd;
+ dev->sock = sock;
+ dev->tail_ptr = dev->buffer;
+
+ if (!prod_name || prod_name[0] == '\0')
+ snprintf(dev->devname, sizeof(dev->devname), "host:%d.%d", bus_num, addr);
+ else
+ pstrcpy(dev->devname, sizeof(dev->devname), prod_name);
+
+ hostdev_link(dev);
+
+ return dev;
+
+fail:
+ if (dev) {
+ qemu_free(dev);
+ }
+ close(fd);
+ close(sock);
+ return NULL;
+}
+
+USBDeviceX *usbd_host_device_open(const char *devname)
+{
+ int bus_num, addr;
+ char product_name[PRODUCT_NAME_SZ];
+
+ if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name),
+ devname) < 0)
+ return NULL;
+
+ if (hostdev_find(bus_num, addr)) {
+ printf("husb: host usb device %d.%d is already open\n", bus_num, addr);
+ return NULL;
+ }
+
+ return usbd_host_device_open_addr(bus_num, addr, product_name);
+}
+
+int usbd_host_device_close(const char *devname)
+{
+ char product_name[PRODUCT_NAME_SZ];
+ int bus_num, addr;
+ USBDeviceX *s;
+
+ if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name),
+ devname) < 0)
+ return -1;
+
+ s = hostdev_find(bus_num, addr);
+ if (s) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static int get_tag_value(char *buf, int buf_size,
+ const char *str, const char *tag,
+ const char *stopchars)
+{
+ const char *p;
+ char *q;
+ p = strstr(str, tag);
+ if (!p)
+ return -1;
+ p += strlen(tag);
+ while (isspace(*p))
+ p++;
+ q = buf;
+ while (*p != '\0' && !strchr(stopchars, *p)) {
+ if ((q - buf) < (buf_size - 1))
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ return q - buf;
+}
+
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+ FILE *f;
+ char line[1024];
+ char buf[1024];
+ int bus_num, addr, speed, device_count, class_id, product_id, vendor_id;
+ int ret;
+ char product_name[512];
+
+ f = fopen(USBDEVFS_PATH "/devices", "r");
+ if (!f) {
+ printf("husb: could not open %s\n", USBDEVFS_PATH "/devices");
+ return 0;
+ }
+ device_count = 0;
+ bus_num = addr = speed = class_id = product_id = vendor_id = 0;
+ ret = 0;
+ for(;;) {
+ if (fgets(line, sizeof(line), f) == NULL)
+ break;
+ if (strlen(line) > 0)
+ line[strlen(line) - 1] = '\0';
+ if (line[0] == 'T' && line[1] == ':') {
+ if (device_count && (vendor_id || product_id)) {
+ /* New device. Add the previously discovered device. */
+ ret = func(opaque, bus_num, addr, class_id, vendor_id,
+ product_id, product_name, speed);
+ if (ret)
+ goto the_end;
+ }
+ if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0)
+ goto fail;
+ bus_num = atoi(buf);
+ if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0)
+ goto fail;
+ addr = atoi(buf);
+ if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0)
+ goto fail;
+ if (!strcmp(buf, "480"))
+ speed = USB_SPEED_HIGH;
+ else if (!strcmp(buf, "1.5"))
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+ product_name[0] = '\0';
+ class_id = 0xff;
+ device_count++;
+ product_id = 0;
+ vendor_id = 0;
+ } else if (line[0] == 'P' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0)
+ goto fail;
+ vendor_id = strtoul(buf, NULL, 16);
+ if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0)
+ goto fail;
+ product_id = strtoul(buf, NULL, 16);
+ } else if (line[0] == 'S' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0)
+ goto fail;
+ pstrcpy(product_name, sizeof(product_name), buf);
+ } else if (line[0] == 'D' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0)
+ goto fail;
+ class_id = strtoul(buf, NULL, 16);
+ }
+fail: ;
+ }
+ if (device_count && (vendor_id || product_id)) {
+ /* Add the last device. */
+ ret = func(opaque, bus_num, addr, class_id, vendor_id,
+ product_id, product_name, speed);
+ }
+the_end:
+ fclose(f);
+ return ret;
+}
+
+typedef struct FindDeviceState {
+ int vendor_id;
+ int product_id;
+ int bus_num;
+ int addr;
+ char product_name[PRODUCT_NAME_SZ];
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
+ int class_id,
+ int vendor_id, int product_id,
+ const char *product_name, int speed)
+{
+ FindDeviceState *s = opaque;
+ if ((vendor_id == s->vendor_id &&
+ product_id == s->product_id) ||
+ (bus_num == s->bus_num &&
+ addr == s->addr)) {
+ pstrcpy(s->product_name, PRODUCT_NAME_SZ, product_name);
+ s->bus_num = bus_num;
+ s->addr = addr;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* the syntax is :
+'bus.addr' (decimal numbers) or
+'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr,
+ char *product_name, int product_name_size,
+ const char *devname)
+{
+ const char *p;
+ int ret;
+ FindDeviceState fs;
+
+ p = strchr(devname, '.');
+ if (p) {
+ *pbus_num = strtoul(devname, NULL, 0);
+ *paddr = strtoul(p + 1, NULL, 0);
+ fs.bus_num = *pbus_num;
+ fs.addr = *paddr;
+ ret = usb_host_scan(&fs, usb_host_find_device_scan);
+ if (ret)
+ pstrcpy(product_name, product_name_size, fs.product_name);
+ return 0;
+ }
+
+ p = strchr(devname, ':');
+ if (p) {
+ fs.vendor_id = strtoul(devname, NULL, 16);
+ fs.product_id = strtoul(p + 1, NULL, 16);
+ ret = usb_host_scan(&fs, usb_host_find_device_scan);
+ if (ret) {
+ *pbus_num = fs.bus_num;
+ *paddr = fs.addr;
+ pstrcpy(product_name, product_name_size, fs.product_name);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+ int class;
+ const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+ { USB_CLASS_AUDIO, "Audio"},
+ { USB_CLASS_COMM, "Communication"},
+ { USB_CLASS_HID, "HID"},
+ { USB_CLASS_HUB, "Hub" },
+ { USB_CLASS_PHYSICAL, "Physical" },
+ { USB_CLASS_PRINTER, "Printer" },
+ { USB_CLASS_MASS_STORAGE, "Storage" },
+ { USB_CLASS_CDC_DATA, "Data" },
+ { USB_CLASS_APP_SPEC, "Application Specific" },
+ { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+ { USB_CLASS_STILL_IMAGE, "Still Image" },
+ { USB_CLASS_CSCID, "Smart Card" },
+ { USB_CLASS_CONTENT_SEC, "Content Security" },
+ { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+ const struct usb_class_info *p;
+ for(p = usb_class_info; p->class != -1; p++) {
+ if (p->class == class)
+ break;
+ }
+ return p->class_name;
+}
+
+static void usb_info_device(int bus_num, int addr, int class_id,
+ int vendor_id, int product_id,
+ const char *product_name,
+ int speed)
+{
+ const char *class_str, *speed_str;
+
+ switch(speed) {
+case USB_SPEED_LOW:
+ speed_str = "1.5";
+ break;
+case USB_SPEED_FULL:
+ speed_str = "12";
+ break;
+case USB_SPEED_HIGH:
+ speed_str = "480";
+ break;
+default:
+ speed_str = "?";
+ break;
+ }
+
+ printf(" Device %d.%d, speed %s Mb/s\n",
+ bus_num, addr, speed_str);
+ class_str = usb_class_str(class_id);
+ if (class_str)
+ printf(" %s:", class_str);
+ else
+ printf(" Class %02x:", class_id);
+ printf(" USB device %04x:%04x", vendor_id, product_id);
+ if (product_name[0] != '\0')
+ printf(", %s", product_name);
+ printf("\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+ int class_id,
+ int vendor_id, int product_id,
+ const char *product_name,
+ int speed)
+{
+ usb_info_device(bus_num, addr, class_id, vendor_id, product_id,
+ product_name, speed);
+ return 0;
+}
+
+void usb_host_info(void)
+{
+ usb_host_scan(NULL, usb_host_info_device);
+}
+
+int handle_submit_urb(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_SUBMIT_URB *nurb = (UNRB_SUBMIT_URB *)hdr;
+ struct usbdevfs_urb *urb;
+ AsyncURB *aurb;
+ int ret;
+
+ aurb = async_alloc();
+ if (!aurb) {
+ dprintf("husb: async malloc failed\n");
+ return USB_RET_NAK;
+ }
+
+ aurb->packet = (USBPacket*)(nurb->urb.opaque);
+
+ urb = &aurb->urb;
+
+ urb->type = nurb->urb.type;
+ urb->endpoint = nurb->urb.endpoint;
+ urb->status = nurb->urb.status;
+ urb->flags = nurb->urb.flags;
+ urb->number_of_packets = nurb->urb.number_of_packets;
+
+ if (urb->number_of_packets) {
+ urb->iso_frame_desc[0].length = nurb->urb.length;
+ }
+
+ urb->buffer_length = nurb->urb.length;
+ urb->buffer = qemu_mallocz(urb->buffer_length);
+
+ memcpy(urb->buffer, nurb->urb.buffer, nurb->urb.length);
+
+#ifdef DEBUG
+ {
+ int x;
+ printf("=== begin dumping urb buffer data ===\n");
+ for (x = 0; x < urb->buffer_length; ++x)
+ printf("%02x ", ((unsigned char*)urb->buffer)[x]);
+ printf("\n=== end dumping urb buffer data ===\n");
+ }
+#endif
+
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+ dprintf("husb: data submit. ep 0x%x len %u aurb %p\n", urb->endpoint, nurb->urb.length, aurb);
+
+ if (ret < 0) {
+ dprintf("husb: submit failed. errno %d\n", errno);
+ async_free(aurb);
+
+ switch(errno) {
+ case ETIMEDOUT:
+ return USB_RET_NAK;
+ case EPIPE:
+ default:
+ return USB_RET_STALL;
+ }
+ }
+}
+
+int handle_set_config(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_SET_CONFIG *nurb = (UNRB_SET_CONFIG *)hdr;
+
+ usb_host_set_config(s, nurb->config);
+}
+
+int handle_set_interface(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_SET_INTERFACE *nurb = (UNRB_SET_INTERFACE *)hdr;
+
+ usb_host_set_interface(s, nurb->iface, nurb->altsetting);
+}
+
+int handle_release_interace(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_RELEASE_INTERFACE *nurb = (UNRB_RELEASE_INTERFACE *)hdr;
+ int ret;
+
+ dprintf("husb: releasing interfaces\n");
+
+ ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &nurb->iface);
+ if (ret < 0) {
+ perror("husb: failed to release interface");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void handle_device_reset(USBDeviceX *dev)
+{
+ dprintf("husb: reset device %u.%u\n", dev->bus_num, dev->addr);
+
+ ioctl(dev->fd, USBDEVFS_RESET);
+}
+
+int handle_claim_interface(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_CLAIM_INTERFACE *nurb = (UNRB_CLAIM_INTERFACE *)hdr;
+ int ret;
+
+ dprintf("husb: claiming interface. interace %d\n", nurb->iface);
+
+ ret = ioctl(s->fd, USBDEVFS_CLAIMINTERFACE, &nurb->iface);
+ if (ret < 0) {
+ if (errno == EBUSY) {
+ printf("husb: update iface. device already grabbed\n");
+ } else {
+ perror("husb: failed to claim interface");
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+int handle_connection_info(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_CONNECTION_INFO *nurb = (UNRB_CONNECTION_INFO *)hdr;
+
+ struct usbdevfs_connectinfo ci;
+ int ret;
+
+ ret = ioctl(s->fd, USBDEVFS_CONNECTINFO, &ci);
+ if (ret < 0) {
+ perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
+ return 0;
+ }
+
+ nurb->devnum = ci.devnum;
+ nurb->slow = ci.slow;
+
+ printf("husb: connect info devnum=%d slow=%d\n", ci.devnum, ci.slow);
+
+ ret = send(s->sock, nurb, sizeof(*nurb), 0);
+
+ return ret;
+}
+
+int handle_io_control(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_IO_CTRL *nurb = (UNRB_IO_CTRL *)hdr;
+
+ struct usbdevfs_ioctl ctrl;
+ int ret;
+
+ ctrl.ioctl_code = nurb->ioctl_code;
+ ctrl.ifno = nurb->ifno;
+
+ ret = ioctl(s->fd, USBDEVFS_IOCTL, &ctrl);
+ if (ret < 0 && errno != ENODATA) {
+ perror("USBDEVFS_IOCTL");
+ return 0;
+ }
+
+ return 1;
+}
+
+int handle_usb_control(USBDeviceX *s, UNRB_HEADER *hdr)
+{
+ UNRB_USB_CONTROL *nurb = (UNRB_USB_CONTROL *)hdr;
+
+ uint8_t configuration;
+ struct usbdevfs_ctrltransfer ct;
+ int ret;
+
+ ct.bRequestType = nurb->type;
+ ct.bRequest = nurb->request;
+ ct.wValue = nurb->value;
+ ct.wIndex = nurb->index;
+ ct.wLength = nurb->length;
+ ct.data = &configuration;
+ ct.timeout = 50;
+
+ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ if (ret < 0) {
+ perror("usb_linux_update_endp_table");
+ return 1;
+ }
+
+ dprintf("USBDEVFS_CONTROL: configuration = %d\n", configuration);
+
+ ret = send(s->sock, &configuration, sizeof(configuration), 0);
+
+ return ret;
+}
+
+void handle_nurb(USBDeviceX *dev, UNRB_HEADER *hdr)
+{
+ switch (hdr->opcode)
+ {
+ case UNRB_OPCODE_NEW_DEVICE:
+ printf("UNRB_OPCODE_NEW_DEVICE\n"); exit(1);
+ break;
+ case UNRB_OPCODE_CLAIM_INTERFACE:
+ handle_claim_interface(dev, hdr);
+ break;
+ case UNRB_OPCODE_CLEAR_HALT:
+ printf("UNRB_OPCODE_CLEAR_HALT\n"); exit(1);
+ break;
+ case UNRB_OPCODE_CONNECTION_INFO:
+ handle_connection_info(dev, hdr);
+ break;
+ case UNRB_OPCODE_DISCARD_URB:
+ printf("UNRB_OPCODE_DISCARD_URB\n"); exit(1);
+ break;
+ case UNRB_OPCODE_IO_CTRL:
+ handle_io_control(dev, hdr);
+ break;
+ case UNRB_OPCODE_GET_INTERFACE:
+ printf("UNRB_OPCODE_GET_INTERFACE\n"); exit(1);
+ break;
+ case UNRB_OPCODE_USB_CONTROL:
+ handle_usb_control(dev, hdr);
+ break;
+ case UNRB_OPCODE_RELEASE_INTERFACE:
+ handle_release_interace(dev, hdr);
+ break;
+ case UNRB_OPCODE_RESET_DEVICE:
+ handle_device_reset(dev);
+ break;
+ case UNRB_OPCODE_SUBMIT_URB:
+ handle_submit_urb(dev, hdr);
+ break;
+ case UNRB_OPCODE_SET_CONFIG:
+ handle_set_config(dev, hdr);
+ break;
+ case UNRB_OPCODE_SET_INTERFACE:
+ handle_set_interface(dev, hdr);
+ break;
+ default:
+ printf("unknown opcode %d!\n", hdr->opcode); exit(1);
+ }
+}
+
+void read_from_network(USBDeviceX *s)
+{
+ UNRB_HEADER *hdr;
+ char *head_ptr = s->buffer;
+ int nread = s->tail_ptr - s->buffer;
+ int size;
+
+ size = recv(s->sock, s->tail_ptr, sizeof(s->buffer) - nread, 0);
+ if ((size == 0) || ((size < 0) && (socket_error() != EWOULDBLOCK))) {
+ dprintf("host disconnect %d\n", socket_error());
+ exit(1);
+ }
+
+ nread += size;
+
+ while (nread > 0) {
+
+ if (nread < sizeof(UNRB_HEADER)) {
+ s->tail_ptr += size;
+ break;
+ }
+
+ hdr = (UNRB_HEADER *)(head_ptr);
+
+ if ((hdr->magic != USB_REMOTE_MAGIC) || (hdr->size == 0)) {
+ printf("bad nurb header\n");
+ exit(1);
+ }
+
+ if (nread < hdr->size) {
+ s->tail_ptr += size;
+ /* need to wait for more data from network. */
+ break;
+ }
+
+ handle_nurb(s, hdr);
+
+ nread -= hdr->size;
+ head_ptr += hdr->size;
+ }
+
+ if ((nread > 0) && (head_ptr != s->buffer)) {
+ memcpy(s->buffer, head_ptr, nread);
+ }
+
+ s->tail_ptr = s->buffer + nread;
+}
+
+static int connect_to_host(int sock, const char *host, uint16_t port)
+{
+ struct sockaddr_in sin;
+ int ret;
+
+ if (!inet_aton(host, &sin.sin_addr)) {
+ printf("error %s!\n", host);
+ return -1;
+ }
+
+ sin.sin_port = htons(port);
+ sin.sin_family = AF_INET;
+
+ ret = connect(sock, (struct sockaddr *)&sin, sizeof(sin));
+ if (ret != 0)
+ {
+ printf("failed to connect %d\n", socket_error());
+ }
+
+ return ret;
+}
+
+static int register_new_device(USBDeviceX *s)
+{
+ UNRB_NEW_DEVICE nurb;
+ int ret;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_NEW_DEVICE;
+ nurb.header.size = sizeof(nurb) + s->descr_len;
+
+ nurb.bus_num = s->bus_num;
+ nurb.addr = s->addr;
+ pstrcpy(nurb.dev_name, sizeof(nurb.dev_name), s->devname);
+ nurb.descr_len = s->descr_len;
+
+ ret = send(s->sock, &nurb, sizeof(nurb), 0);
+ if (ret > 0) {
+ ret = send(s->sock, s->descr, s->descr_len, 0);
+ }
+
+ return ret;
+}
+
+static void main_loop()
+{
+ USBDeviceX *dev = hostdev_list;
+ fd_set rfds, wfds;
+ int ret, nfds;
+
+ nfds = -1;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ while (dev) {
+ FD_SET(dev->sock, &rfds);
+ FD_SET(dev->fd, &wfds);
+ nfds = MAX(nfds, MAX(dev->fd, dev->sock));
+ dev = dev->next;
+ }
+
+ ret = select(nfds + 1, &rfds, &wfds, NULL, NULL);
+ if (ret > 0) {
+ dev = hostdev_list;
+ while (dev) {
+ if (FD_ISSET(dev->sock, &rfds)) {
+ read_from_network(dev);
+ }
+ if (FD_ISSET(dev->fd, &wfds)) {
+ async_complete(dev);
+ }
+ dev = dev->next;
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ const char *host;
+ uint16_t port;
+ const char *device;
+
+ if (argc < 4) {
+ printf("%s: host port (bus.addr | vendor:product)\n\n", argv[0]);
+ printf("\tbus.addr - (decimal)\n");
+ printf("\tvendor:product - (hexadecimal)\n");
+ return EXIT_SUCCESS;
+ }
+
+ host = argv[1];
+ port = atoi(argv[2]);
+ device = argv[3];
+
+ USBDeviceX *dev = usbd_host_device_open(device);
+ if (!dev) {
+ return;
+ }
+
+ if (connect_to_host(dev->sock, host, port) != 0) {
+ return;
+ }
+
+ socket_set_nonblock(dev->sock);
+
+ if (register_new_device(dev) < 0) {
+ printf("failed to register device %s %d\n", dev->devname, socket_error());
+ return;
+ }
+
+ while (hostdev_list) {
+ main_loop();
+ }
+
+ shutdown(dev->sock, SHUT_RDWR);
+ closesocket(dev->sock);
+
+ return EXIT_SUCCESS;
+}
diff --git a/usb-linux.c b/usb-linux.c
index c5da5b5..24a7cb8 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -28,6 +28,7 @@
#include "qemu-common.h"
#include "qemu-timer.h"
+#include "qemu_socket.h"
#include "console.h"
#if defined(__linux__)
@@ -38,6 +39,7 @@
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#include "hw/usb.h"
+#include "usb-remote.h"
/* We redefine it to avoid version problems */
struct usb_ctrltransfer {
@@ -58,12 +60,19 @@ struct usb_ctrlrequest {
uint16_t wLength;
};
+typedef int __ioctl(int fd, unsigned long int request, ...);
+
typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
int vendor_id, int product_id,
const char *product_name, int speed);
static int usb_host_find_device(int *pbus_num, int *paddr,
char *product_name, int product_name_size,
const char *devname);
+
+extern int usb_remote_ioctl(int fd, unsigned long int request, ...);
+
+extern int parse_host_port(struct sockaddr_in *saddr, const char *str);
+
//#define DEBUG
#ifdef DEBUG
@@ -119,9 +128,20 @@ typedef struct USBHostDevice {
int bus_num;
int addr;
+ __ioctl *ioctl;
+
struct USBHostDevice *next;
} USBHostDevice;
+typedef struct USBRemoteDevice {
+ USBHostDevice hdev;
+
+ /* Network buffer */
+ char *read_ptr;
+ char buffer[4096];
+
+} USBRemoteDevice;
+
static int is_isoc(USBHostDevice *s, int ep)
{
return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO;
@@ -229,7 +249,7 @@ static void async_complete(void *opaque)
while (1) {
USBPacket *p;
- int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+ int r = s->ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
if (r < 0) {
if (errno == EAGAIN)
return;
@@ -282,7 +302,7 @@ static void async_cancel(USBPacket *unused, void *opaque)
/* Mark it as dead (see async_complete above) */
aurb->packet = NULL;
- int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+ int r = s->ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
if (r < 0) {
dprintf("husb: async. discard urb failed errno %d\n", errno);
}
@@ -339,7 +359,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
for (interface = 0; interface < nb_interfaces; interface++) {
ctrl.ioctl_code = USBDEVFS_DISCONNECT;
ctrl.ifno = interface;
- ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+ ret = dev->ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
if (ret < 0 && errno != ENODATA) {
perror("USBDEVFS_DISCONNECT");
goto fail;
@@ -350,7 +370,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
/* XXX: only grab if all interfaces are free */
for (interface = 0; interface < nb_interfaces; interface++) {
- ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+ ret = dev->ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
if (ret < 0) {
if (errno == EBUSY) {
printf("husb: update iface. device already grabbed\n");
@@ -377,7 +397,7 @@ static int usb_host_release_interfaces(USBHostDevice *s)
dprintf("husb: releasing interfaces\n");
for (i = 0; i < s->ninterfaces; i++) {
- ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
+ ret = s->ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
if (ret < 0) {
perror("husb: failed to release interface");
return 0;
@@ -393,7 +413,7 @@ static void usb_host_handle_reset(USBDevice *dev)
dprintf("husb: reset device %u.%u\n", s->bus_num, s->addr);
- ioctl(s->fd, USBDEVFS_RESET);
+ s->ioctl(s->fd, USBDEVFS_RESET);
usb_host_claim_interfaces(s, s->configuration);
}
@@ -440,7 +460,7 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
urb->endpoint = p->devep;
if (is_halted(s, p->devep)) {
- ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
+ ret = s->ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
if (ret < 0) {
dprintf("husb: failed to clear halt. ep 0x%x errno %d\n",
urb->endpoint, errno);
@@ -465,7 +485,7 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
urb->usercontext = s;
- ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+ ret = s->ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
dprintf("husb: data submit. ep 0x%x len %u aurb %p\n", urb->endpoint, p->len, aurb);
@@ -505,7 +525,7 @@ static int usb_host_set_config(USBHostDevice *s, int config)
{
usb_host_release_interfaces(s);
- int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+ int ret = s->ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
dprintf("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
@@ -523,7 +543,7 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
si.interface = iface;
si.altsetting = alt;
- ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+ ret = s->ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
dprintf("husb: ctrl set iface %d altset %d ret %d errno %d\n",
iface, alt, ret, errno);
@@ -592,7 +612,7 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
urb->usercontext = s;
- ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+ ret = s->ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
dprintf("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
@@ -782,7 +802,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
ct.data = &configuration;
ct.timeout = 50;
- ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ ret = s->ioctl(s->fd, USBDEVFS_CONTROL, &ct);
if (ret < 0) {
perror("usb_linux_update_endp_table");
return 1;
@@ -823,7 +843,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
ct.data = &alt_interface;
ct.timeout = 50;
- ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ ret = s->ioctl(s->fd, USBDEVFS_CONTROL, &ct);
if (ret < 0) {
perror("usb_linux_update_endp_table");
return 1;
@@ -916,6 +936,7 @@ static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *p
#endif
dev->fd = fd;
+ dev->ioctl = ioctl;
/*
* Initial configuration is -1 which makes us claim first
@@ -926,7 +947,7 @@ static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *p
if (!usb_host_claim_interfaces(dev, -1))
goto fail;
- ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
+ ret = dev->ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
if (ret < 0) {
perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
goto fail;
@@ -1483,6 +1504,279 @@ void usb_host_info(void)
}
}
+static int usb_remote_recv_nurb(USBRemoteDevice *dev, int op)
+{
+ UNRB_HEADER *hdr;
+ int nread = dev->read_ptr - dev->buffer;
+ int size;
+
+ size = recv(dev->hdev.fd, dev->read_ptr, sizeof(dev->buffer) - nread, 0);
+ if ((size == 0) || ((size < 0) && (socket_error() != EWOULDBLOCK))) {
+ return -1;
+ }
+
+ dev->read_ptr += size;
+
+ nread = dev->read_ptr - dev->buffer;
+ if (nread < sizeof(UNRB_HEADER)) {
+ return 0;
+ }
+
+ hdr = (UNRB_HEADER *)(dev->buffer);
+
+ if ((hdr->magic != USB_REMOTE_MAGIC) || (hdr->size == 0) || (hdr->opcode != op)) {
+ return -1;
+ }
+
+ if (nread < hdr->size) {
+ /* need to wait for more data from network. */
+ return 0;
+ }
+
+ /* rewind buffer head after all data was received. */
+ dev->read_ptr = dev->buffer;
+
+ return 1;
+}
+
+static void remote_async_complete(void *opaque)
+{
+ USBRemoteDevice *s = opaque;
+ UNRB_SUBMIT_URB *nurb;
+ AsyncURB *aurb;
+ USBPacket *p;
+ int ret, len;
+
+ ret = usb_remote_recv_nurb(s, UNRB_OPCODE_SUBMIT_URB);
+ if (ret == 0) {
+ return;
+ } else if (ret < 0) {
+ printf("rusb: device %d.%d disconnected\n", s->hdev.bus_num, s->hdev.dev.addr);
+ usb_device_del_addr(0, s->hdev.dev.addr);
+ return;
+ }
+
+ nurb = (UNRB_SUBMIT_URB *)(s->buffer);
+
+ aurb = (AsyncURB *)(nurb->urb.opaque);
+
+ aurb->urb.type = nurb->urb.type;
+ aurb->urb.status = nurb->urb.status;
+ aurb->urb.actual_length = nurb->urb.length;
+
+ memcpy(aurb->urb.buffer, nurb->urb.buffer, aurb->urb.buffer_length);
+
+ p = aurb->packet;
+
+ dprintf("husb: async completed. aurb %p status %d alen %d\n",
+ aurb, aurb->urb.status, aurb->urb.actual_length);
+
+ if (p) {
+ switch (aurb->urb.status) {
+ case 0:
+ p->len = aurb->urb.actual_length;
+ if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL)
+ async_complete_ctrl(&s->hdev, p);
+ break;
+
+ case -EPIPE:
+ set_halt(&s->hdev, p->devep);
+ /* fall through */
+ default:
+ p->len = USB_RET_NAK;
+ break;
+ }
+
+ usb_packet_complete(p);
+ }
+
+ async_free(aurb);
+}
+
+static void usb_remote_device_open(USBRemoteDevice *s, UNRB_NEW_DEVICE *nurb)
+{
+ USBHostDevice *dev = &s->hdev;
+ struct usbdevfs_connectinfo ci;
+ int ret;
+
+ dev->bus_num = nurb->bus_num;
+ dev->addr = nurb->addr;
+
+ printf("rusb: open device %d.%d\n", nurb->bus_num, nurb->addr);
+
+ /* "read" the device description */
+ dev->descr_len = nurb->descr_len;
+ memcpy(dev->descr, nurb->descr, nurb->descr_len);
+
+#ifdef DEBUG
+ {
+ int x;
+ printf("=== begin dumping device descriptor data ===\n");
+ for (x = 0; x < dev->descr_len; x++)
+ printf("%02x ", dev->descr[x]);
+ printf("\n=== end dumping device descriptor data ===\n");
+ }
+#endif
+
+ /*
+ * Initial configuration is -1 which makes us claim first
+ * available config. We used to start with 1, which does not
+ * always work. I've seen devices where first config starts
+ * with 2.
+ */
+ if (!usb_host_claim_interfaces(dev, -1))
+ goto fail;
+
+ ret = dev->ioctl(dev->fd, USBDEVFS_CONNECTINFO, &ci);
+ if (ret < 0) {
+ perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
+ goto fail;
+ }
+
+ printf("rusb: grabbed usb device %d.%d\n", nurb->bus_num, nurb->addr);
+
+ ret = usb_linux_update_endp_table(dev);
+ if (ret)
+ goto fail;
+
+ if (ci.slow)
+ dev->dev.speed = USB_SPEED_LOW;
+ else
+ dev->dev.speed = USB_SPEED_HIGH;
+
+ dev->dev.handle_packet = usb_host_handle_packet;
+ dev->dev.handle_reset = usb_host_handle_reset;
+ dev->dev.handle_destroy = usb_host_handle_destroy;
+
+ if (!nurb->dev_name || nurb->dev_name[0] == '\0')
+ snprintf(dev->dev.devname, sizeof(dev->dev.devname),
+ "host:%d.%d", nurb->bus_num, nurb->addr);
+ else
+ pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
+ nurb->dev_name);
+
+ qemu_set_fd_handler(dev->fd, remote_async_complete, NULL, s);
+
+ hostdev_link(dev);
+
+ // Now that we know which device was arrived we can add it to QEmu.
+ usb_device_add_dev(&dev->dev);
+
+ return;
+fail:
+ close(dev->fd);
+ qemu_free(dev);
+}
+
+static void usb_remote_device_init(void *opaque)
+{
+ USBRemoteDevice *s = opaque;
+ UNRB_NEW_DEVICE *nurb;
+ int ret;
+
+ ret = usb_remote_recv_nurb(s, UNRB_OPCODE_NEW_DEVICE);
+ if (ret == 1) {
+ nurb = (UNRB_NEW_DEVICE *)(s->buffer);
+ usb_remote_device_open(s, nurb);
+ } else if (ret == -1) {
+ qemu_set_fd_handler(s->hdev.fd, NULL, NULL, NULL);
+ close(s->hdev.fd);
+ qemu_free(s);
+ }
+}
+
+static USBRemoteDevice* usb_remote_device_new(int sock)
+{
+ USBRemoteDevice *dev;
+ const int nodelay = 1;
+
+ dev = qemu_mallocz(sizeof(USBRemoteDevice));
+ if (!dev) {
+ return NULL;
+ }
+
+ socket_set_nonblock(sock);
+
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char*)&nodelay, sizeof(nodelay));
+
+ dev->hdev.fd = sock;
+ dev->hdev.ioctl = usb_remote_ioctl;
+
+ dev->read_ptr = dev->buffer;
+
+ qemu_set_fd_handler(dev->hdev.fd, usb_remote_device_init, NULL, dev);
+
+ return dev;
+}
+
+static void usb_remote_accept(void *opaque)
+{
+ int sock = opaque;
+ USBRemoteDevice *dev;
+ struct sockaddr_in saddr;
+ socklen_t addrlen;
+ int csock;
+
+ for (;;) {
+ addrlen = sizeof(saddr);
+ csock = accept(sock, (struct sockaddr *)&saddr, &addrlen);
+ if ((csock < 0) && (errno != EINTR)) {
+ return;
+ } else if (csock >= 0) {
+ break;
+ }
+ }
+
+ dev = usb_remote_device_new(csock);
+ if (!dev) {
+ closesocket(csock);
+ } else {
+ dprintf("rusb: connection from %s:%d\n",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ }
+}
+
+int usb_remote_start(const char *host_str)
+{
+ const int reuse = 1;
+ int sock;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0) {
+ return 0;
+ }
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock == -1) {
+ fprintf(stderr, "rusb: socket() failed (%d)\n", socket_error());
+ return 0;
+ }
+
+ socket_set_nonblock(sock);
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&reuse, sizeof(reuse));
+
+ if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
+ fprintf(stderr, "rusb: bind() failed (%d)\n", socket_error());
+ goto fail;
+ }
+
+ if (listen(sock, 1) == -1) {
+ fprintf(stderr, "rusb: listen() failed (%d)\n", socket_error());
+ goto fail;
+ }
+
+ qemu_set_fd_handler(sock, usb_remote_accept, NULL, sock);
+
+ return 1;
+
+fail:
+ closesocket(sock);
+ return 0;
+}
+
#else
#include "hw/usb.h"
@@ -1503,4 +1797,10 @@ int usb_host_device_close(const char *devname)
return 0;
}
+int usb_remote_start(const char *host_str)
+{
+ term_printf("USB remote devices not supported\n");
+ return 0;
+}
+
#endif
diff --git a/usb-remote.c b/usb-remote.c
new file mode 100644
index 0000000..75c345b
--- /dev/null
+++ b/usb-remote.c
@@ -0,0 +1,324 @@
+/*
+* Linux remote USB redirector
+*
+* Copyright (c) 2005 Fabrice Bellard
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "usb-remote.h"
+
+#if defined(__linux__)
+
+#include <sys/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+#define DEBUG
+
+#ifdef DEBUG
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+static void fill_nurb(UNRB_URB *nurb, struct usbdevfs_urb *urb)
+{
+ nurb->opaque = (uint64_t)urb;
+ nurb->type = urb->type;
+ nurb->endpoint = urb->endpoint;
+ nurb->status = urb->status;
+ nurb->flags = urb->flags;
+ nurb->length = urb->buffer_length;
+ nurb->number_of_packets = urb->number_of_packets;
+}
+
+static int usb_remote_claim_interface(int fd, int interface)
+{
+ UNRB_CLAIM_INTERFACE nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_CLAIM_INTERFACE;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.iface = interface;
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_clear_halt(int fd, char endpoint)
+{
+ UNRB_CLEAR_HALT nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_CLEAR_HALT;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.endpoint = endpoint;
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_connect_info(int fd, struct usbdevfs_connectinfo *ci)
+{
+ UNRB_CONNECTION_INFO nurb;
+ int ret;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_CONNECTION_INFO;
+ nurb.header.size = sizeof(nurb);
+
+ ret = send(fd, &nurb, sizeof(nurb), 0);
+
+ if (ret > 0) {
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+
+ ret = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if ((ret > 0) && FD_ISSET(fd, &rfds)) {
+ ret = recv(fd, &nurb, sizeof(nurb), MSG_WAITALL);
+ }
+ }
+
+ if (ret > 0) {
+ ci->devnum = nurb.devnum;
+ ci->slow = nurb.slow;
+ }
+
+ return ret;
+}
+
+static int usb_remote_control(int fd, struct usbdevfs_ctrltransfer *ct)
+{
+ UNRB_USB_CONTROL nurb;
+ int ret;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_USB_CONTROL;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.type = ct->bRequestType;
+ nurb.request = ct->bRequest;
+ nurb.value = ct->wValue;
+ nurb.index = ct->wIndex;
+ nurb.length = ct->wLength;
+ nurb.timeout = ct->timeout;
+
+ ret = send(fd, &nurb, sizeof(nurb), 0);
+
+ if (ret > 0) {
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+
+ ret = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if ((ret > 0) && FD_ISSET(fd, &rfds)) {
+ ret = recv(fd, ct->data, ct->wLength, MSG_WAITALL);
+ }
+ }
+
+ return ret;
+}
+
+static int usb_remote_discard_urb(int fd, struct usbdevfs_urb *urb)
+{
+ UNRB_DISCARD_URB nurb;
+ int ret;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_DISCARD_URB;
+ nurb.header.size = sizeof(nurb) + urb->buffer_length;
+
+ fill_nurb(&nurb.urb, urb);
+
+ ret = send(fd, &nurb, sizeof(nurb), 0);
+ if (ret > 0) {
+ ret = send(fd, urb->buffer, urb->buffer_length, 0);
+ }
+
+ return ret;
+}
+
+static int usb_remote_usb_ioctl(int fd, struct usbdevfs_ioctl *ctrl)
+{
+ UNRB_IO_CTRL nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_IO_CTRL;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.ifno = ctrl->ifno;
+ nurb.ioctl_code = ctrl->ioctl_code;
+ nurb.data = (uint64_t)(ctrl->data);
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_read_urb(int fd, struct usbdevfs_urb *urb)
+{
+ /* Could be nice to implement but the network buffer is out of reach when
+ we have only the file descriptor identifier. */
+ return -1;
+}
+
+static int usb_remote_release_interface(int fd, int interface)
+{
+ UNRB_RELEASE_INTERFACE nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_RELEASE_INTERFACE;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.iface = interface;
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_reset(int fd)
+{
+ UNRB_RESET_DEVICE nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_RESET_DEVICE;
+ nurb.header.size = sizeof(nurb);
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_set_config(int fd, int config)
+{
+ UNRB_SET_CONFIG nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_SET_CONFIG;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.config = config;
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_set_interface(int fd, struct usbdevfs_setinterface *si)
+{
+ UNRB_SET_INTERFACE nurb;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_SET_INTERFACE;
+ nurb.header.size = sizeof(nurb);
+
+ nurb.iface = si->interface;
+ nurb.altsetting = si->altsetting;
+
+ return send(fd, &nurb, sizeof(nurb), 0);
+}
+
+static int usb_remote_submit_urb(int fd, struct usbdevfs_urb *urb)
+{
+ UNRB_SUBMIT_URB nurb;
+ int ret;
+
+ nurb.header.magic = USB_REMOTE_MAGIC;
+ nurb.header.opcode = UNRB_OPCODE_SUBMIT_URB;
+ nurb.header.size = sizeof(nurb) + urb->buffer_length;
+
+ fill_nurb(&nurb.urb, urb);
+
+ ret = send(fd, &nurb, sizeof(nurb), 0);
+ if (ret > 0) {
+ ret = send(fd, urb->buffer, urb->buffer_length, 0);
+ }
+
+ return ret;
+}
+
+int usb_remote_ioctl(int fd, unsigned long int request, ...)
+{
+ va_list args;
+ va_start(args, request);
+ int ret;
+
+ switch (request) {
+ case USBDEVFS_CLAIMINTERFACE:
+ ret = usb_remote_claim_interface(fd, *va_arg(args, int*));
+ break;
+ case USBDEVFS_CLEAR_HALT:
+ ret = usb_remote_clear_halt(fd, *va_arg(args, char*));
+ break;
+ case USBDEVFS_CONNECTINFO:
+ ret = usb_remote_connect_info(fd, va_arg(args, struct usbdevfs_connectinfo *));
+ break;
+ case USBDEVFS_CONTROL:
+ ret = usb_remote_control(fd, va_arg(args, struct usbdevfs_ctrltransfer *));
+ break;
+ case USBDEVFS_DISCARDURB:
+ ret = usb_remote_discard_urb(fd, va_arg(args, struct usbdevfs_urb *));
+ break;
+ case USBDEVFS_IOCTL:
+ ret = usb_remote_usb_ioctl(fd, va_arg(args, struct usbdevfs_ioctl *));
+ break;
+ case USBDEVFS_REAPURBNDELAY:
+ ret = usb_remote_read_urb(fd, va_arg(args, struct usbdevfs_urb *));
+ break;
+ case USBDEVFS_RELEASEINTERFACE:
+ ret = usb_remote_release_interface(fd, *va_arg(args, int*));
+ break;
+ case USBDEVFS_RESET:
+ ret = usb_remote_reset(fd);
+ break;
+ case USBDEVFS_SETCONFIGURATION:
+ ret = usb_remote_set_config(fd, *va_arg(args, int*));
+ break;
+ case USBDEVFS_SETINTERFACE:
+ ret = usb_remote_set_interface(fd, va_arg(args, struct usbdevfs_setinterface *));
+ break;
+ case USBDEVFS_SUBMITURB:
+ ret = usb_remote_submit_urb(fd, va_arg(args, struct usbdevfs_urb *));
+ break;
+ default:
+ fprintf(stderr, "husb: unknown ioctl() request %ld\n", request);
+ ret = -1;
+ }
+
+ va_end(args);
+
+ return ret;
+}
+
+#else
+
+int usb_remote_ioctl(int fd, unsigned long int request, ...)
+{
+ return -1;
+}
+
+#endif // defined(__linux__)
diff --git a/usb-remote.h b/usb-remote.h
new file mode 100644
index 0000000..52694f6
--- /dev/null
+++ b/usb-remote.h
@@ -0,0 +1,160 @@
+/*
+ * Linux remote USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _USB_REMOTE_H
+#define _USB_REMOTE_H
+
+#define USB_REMOTE_MAGIC (*(uint32_t*)"RUSB")
+
+#define PRODUCT_NAME_SZ 32
+
+enum {
+ UNRB_OPCODE_NEW_DEVICE,
+ UNRB_OPCODE_CLAIM_INTERFACE,
+ UNRB_OPCODE_CLEAR_HALT,
+ UNRB_OPCODE_CONNECTION_INFO,
+ UNRB_OPCODE_DISCARD_URB,
+ UNRB_OPCODE_IO_CTRL,
+ UNRB_OPCODE_GET_INTERFACE,
+ UNRB_OPCODE_USB_CONTROL,
+ UNRB_OPCODE_RELEASE_INTERFACE,
+ UNRB_OPCODE_RESET_DEVICE,
+ UNRB_OPCODE_SUBMIT_URB,
+ UNRB_OPCODE_SET_CONFIG,
+ UNRB_OPCODE_SET_INTERFACE,
+};
+
+typedef struct UNRB_HEADER {
+ uint32_t magic;
+ uint32_t size;
+ uint32_t opcode;
+
+} __attribute__((packed)) UNRB_HEADER, *PUNRB_HEADER;
+
+typedef struct UNRB_URB {
+ uint64_t opaque;
+ uint8_t type;
+ uint8_t endpoint;
+ int32_t status;
+ int32_t flags;
+ int32_t number_of_packets;
+ int32_t length;
+ uint8_t buffer[];
+
+} __attribute__((packed)) UNRB_URB, *PUNRB_URB;
+
+typedef struct UNRB_NEW_DEVICE {
+ UNRB_HEADER header;
+ int32_t bus_num;
+ int32_t addr;
+ uint8_t dev_name[PRODUCT_NAME_SZ];
+ int32_t descr_len;
+ uint8_t descr[];
+
+} __attribute__((packed)) UNRB_NEW_DEVICE, *PUNRB_NEW_DEVICE;
+
+typedef struct UNRB_CLAIM_INTERFACE {
+ UNRB_HEADER header;
+ uint32_t iface;
+
+} __attribute__((packed)) UNRB_CLAIM_INTERFACE, *PUNRB_CLAIM_INTERFACE;
+
+typedef struct UNRB_CLEAR_HALT {
+ UNRB_HEADER header;
+ uint8_t endpoint;
+
+} __attribute__((packed)) UNRB_CLEAR_HALT, *PUNRB_CLEAR_HALT;
+
+typedef struct UNRB_CONNECTION_INFO {
+ UNRB_HEADER header;
+ uint32_t devnum;
+ uint8_t slow;
+
+} __attribute__((packed)) UNRB_CONNECTION_INFO, *PUNRB_CONNECTION_INFO;
+
+typedef struct UNRB_DISCARD_URB {
+ UNRB_HEADER header;
+ UNRB_URB urb;
+
+} __attribute__((packed)) UNRB_DISCARD_URB, *PUNRB_DISCARD_URB;
+
+typedef struct UNRB_IO_CTRL {
+ UNRB_HEADER header;
+ uint32_t ifno;
+ uint32_t ioctl_code;
+ uint64_t data;
+
+} __attribute__((packed)) UNRB_IO_CTRL, *PUNRB_IO_CTRL;
+
+typedef struct UNRB_GET_INTERFACE {
+ UNRB_HEADER header;
+ uint8_t configuration;
+ uint32_t number_of_ifaces;
+ uint8_t alt_iface[];
+
+} __attribute__((packed)) UNRB_GET_INTERFACE, *PUNRB_GET_INTERFACE;
+
+typedef struct UNRB_USB_CONTROL {
+ UNRB_HEADER header;
+ uint8_t type;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+ uint32_t timeout; /* in milliseconds */
+ uint8_t data[];
+
+} __attribute__((packed)) UNRB_USB_CONTROL, *PUNRB_USB_CONTROL;
+
+typedef struct UNRB_SUBMIT_URB {
+ UNRB_HEADER header;
+ UNRB_URB urb;
+
+} __attribute__((packed)) UNRB_SUBMIT_URB, *PUNRB_SUBMIT_URB;
+
+typedef struct UNRB_RELEASE_INTERFACE {
+ UNRB_HEADER header;
+ uint32_t iface;
+
+} __attribute__((packed)) UNRB_RELEASE_INTERFACE, *PUNRB_RELEASE_INTERFACE;
+
+typedef struct UNRB_RESET_DEVICE {
+ UNRB_HEADER header;
+
+} __attribute__((packed)) UNRB_RESET_DEVICE, *PUNRB_RESET_DEVICE;
+
+typedef struct UNRB_SET_CONFIG {
+ UNRB_HEADER header;
+ uint32_t config;
+
+} __attribute__((packed)) UNRB_SET_CONFIG, *PUNRB_SET_CONFIG;
+
+typedef struct UNRB_SET_INTERFACE {
+ UNRB_HEADER header;
+ uint32_t iface;
+ uint32_t altsetting;
+
+} __attribute__((packed)) UNRB_SET_INTERFACE, *PUNRB_SET_INTERFACE;
+
+#endif // _USB_REMOTE_H
diff --git a/vl.c b/vl.c
index c94fdc0..b140bd3 100644
--- a/vl.c
+++ b/vl.c
@@ -7902,6 +7902,8 @@ static void help(int exitcode)
#endif
"-usb enable the USB driver (will be the default soon)\n"
"-usbdevice name add the host or guest USB device 'name'\n"
+ "-usbremote hostname:port\n"
+ " enable a remote USB server (USB over network)\n"
#if defined(TARGET_PPC) || defined(TARGET_SPARC)
"-g WxH[xDEPTH] Set the initial graphical resolution and depth\n"
#endif
@@ -8080,6 +8082,7 @@ enum {
QEMU_OPTION_win2k_hack,
QEMU_OPTION_usb,
QEMU_OPTION_usbdevice,
+ QEMU_OPTION_usbremote,
QEMU_OPTION_smp,
QEMU_OPTION_vnc,
QEMU_OPTION_no_acpi,
@@ -8181,6 +8184,7 @@ static const QEMUOption qemu_options[] = {
{ "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
{ "win2k-hack", 0, QEMU_OPTION_win2k_hack },
{ "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
+ { "usbremote", HAS_ARG, QEMU_OPTION_usbremote },
{ "smp", HAS_ARG, QEMU_OPTION_smp },
{ "vnc", HAS_ARG, QEMU_OPTION_vnc },
#ifdef CONFIG_CURSES
@@ -8497,6 +8501,7 @@ int main(int argc, char **argv)
const char *cpu_model;
const char *usb_devices[MAX_USB_CMDLINE];
int usb_devices_index;
+ const char *usb_hostname = NULL;
int fds[2];
int tb_size;
const char *pid_file = NULL;
@@ -8994,6 +8999,10 @@ int main(int argc, char **argv)
usb_devices[usb_devices_index] = optarg;
usb_devices_index++;
break;
+ case QEMU_OPTION_usbremote:
+ usb_enabled = 1;
+ usb_hostname = optarg;
+ break;
case QEMU_OPTION_smp:
smp_cpus = atoi(optarg);
if (smp_cpus < 1 || smp_cpus > MAX_CPUS) {
@@ -9422,6 +9431,10 @@ int main(int argc, char **argv)
usb_devices[i]);
}
}
+
+ if (usb_hostname) {
+ usb_remote_start(usb_hostname);
+ }
}
if (display_state.dpy_refresh) {
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 11:43 [Qemu-devel] [PATCH] USB over network Gal Hammer
@ 2008-10-06 12:09 ` Paul Brook
2008-10-06 12:22 ` Paul Brook
2008-10-06 13:15 ` Gal Hammer
2008-10-06 12:32 ` Daniel P. Berrange
2008-10-06 15:05 ` Blue Swirl
2 siblings, 2 replies; 13+ messages in thread
From: Paul Brook @ 2008-10-06 12:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Gal Hammer
> Attached is a preliminary patch which add QEmu the ability to use local
> USB devices over network. It should work with DOK devices and might work
> with web cameras.
Apart from anything else, it's missing documentation.
It looks like you have to start qemu with magic options on the remote machine?
This seems a very bad solution. The remote stub should be a separate
application which communicated via a documented protocol. Preferably this
should be a standardised protocol that is/can be suppoorted by third party
devices.
Paul
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 12:09 ` Paul Brook
@ 2008-10-06 12:22 ` Paul Brook
2008-10-06 12:42 ` Dor Laor
2008-10-06 13:15 ` Gal Hammer
1 sibling, 1 reply; 13+ messages in thread
From: Paul Brook @ 2008-10-06 12:22 UTC (permalink / raw)
To: qemu-devel; +Cc: Gal Hammer
On Monday 06 October 2008, Paul Brook wrote:
> > Attached is a preliminary patch which add QEmu the ability to use local
> > USB devices over network. It should work with DOK devices and might work
> > with web cameras.
>
> Apart from anything else, it's missing documentation.
>
> It looks like you have to start qemu with magic options on the remote
> machine? This seems a very bad solution. The remote stub should be a
> separate application which communicated via a documented protocol.
> Preferably this should be a standardised protocol that is/can be suppoorted
> by third party devices.
As a specific example, have you looked at http://usbip.sourceforge.net/ ?
It doesn't seem to be particularly active, but it's at least a useful
reference point to see whether your remote protocol is sane.
Paul
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 12:22 ` Paul Brook
@ 2008-10-06 12:42 ` Dor Laor
0 siblings, 0 replies; 13+ messages in thread
From: Dor Laor @ 2008-10-06 12:42 UTC (permalink / raw)
To: qemu-devel; +Cc: Gal Hammer
[-- Attachment #1: Type: text/plain, Size: 1396 bytes --]
Paul Brook wrote:
> On Monday 06 October 2008, Paul Brook wrote:
>
>>> Attached is a preliminary patch which add QEmu the ability to use local
>>> USB devices over network. It should work with DOK devices and might work
>>> with web cameras.
>>>
>> Apart from anything else, it's missing documentation.
>>
>> It looks like you have to start qemu with magic options on the remote
>> machine? This seems a very bad solution. The remote stub should be a
>> separate application which communicated via a documented protocol.
>> Preferably this should be a standardised protocol that is/can be suppoorted
>> by third party devices.
>>
>
> As a specific example, have you looked at http://usbip.sourceforge.net/ ?
> It doesn't seem to be particularly active, but it's at least a useful
> reference point to see whether your remote protocol is sane.
>
> Pau
Indeed its missing documentation and the protocol is far from being
defined, plus it should be a separate application.
But, its a good step forward in usb remoting, the protocol leverage
existing knowledge from usbip but it is an opportunity
to advance in cumulative steps.
The intention is to make the usb remoting transparent to the guest.
The application should be stand alone but can share code with qemu,
maybe similar to qemu-img application (can it?).
Thanks for the feedback, any additional feedback is welcomed.
[-- Attachment #2: Type: text/html, Size: 1946 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [Qemu-devel] [PATCH] USB over network
2008-10-06 12:09 ` Paul Brook
2008-10-06 12:22 ` Paul Brook
@ 2008-10-06 13:15 ` Gal Hammer
2008-10-06 13:32 ` Paul Brook
2008-10-06 13:41 ` Anthony Liguori
1 sibling, 2 replies; 13+ messages in thread
From: Gal Hammer @ 2008-10-06 13:15 UTC (permalink / raw)
To: qemu-devel
> Apart from anything else, it's missing documentation.
I am aware of that. I'll document the work I've done so far and include
the open issues that you (and others) wrote.
> It looks like you have to start qemu with magic options on the remote
> machine?
No, qemu should be started with "-usbremote host:port" command line
option. This will start a listener which wait for incoming remote
connections. The remote connection is established with the qemu-usbd
application. The qemu-usbd is executed on the local machine, the one
that the USB device is attached to.
> This seems a very bad solution. The remote stub should be a separate
> application which communicated via a documented protocol. Preferably
> this
> should be a standardised protocol that is/can be suppoorted by third
> party
> devices.
I wouldn't say that this is a "very bad" solution. A "bad" solution is
good enough. :-)
At the moment, the protocol is a simple
what-usb-linux-want-usb-linux-get approach. I wrote it that way because
I couldn't find a standardized protocol for an USB-over-IP (I'm familiar
with http://usbip.sourceforge.net project). This also allowed me to use
most of the code in usb-linux.c file thereby reducing code duplications.
Gal.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 13:15 ` Gal Hammer
@ 2008-10-06 13:32 ` Paul Brook
2008-10-06 13:41 ` Anthony Liguori
1 sibling, 0 replies; 13+ messages in thread
From: Paul Brook @ 2008-10-06 13:32 UTC (permalink / raw)
To: qemu-devel; +Cc: Gal Hammer
> > It looks like you have to start qemu with magic options on the remote
> > machine?
>
> No, qemu should be started with "-usbremote host:port" command line
> option. This will start a listener which wait for incoming remote
> connections. The remote connection is established with the qemu-usbd
> application. The qemu-usbd is executed on the local machine, the one
> that the USB device is attached to.
I don't see any code for building qemu-usbd.
> At the moment, the protocol is a simple
> what-usb-linux-want-usb-linux-get approach
The wire protocol (and the qemu end of the code) should not be linux specific.
Was this code really written by Fabrice Bellard?
Paul
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 13:15 ` Gal Hammer
2008-10-06 13:32 ` Paul Brook
@ 2008-10-06 13:41 ` Anthony Liguori
2008-10-06 14:20 ` Gal Hammer
1 sibling, 1 reply; 13+ messages in thread
From: Anthony Liguori @ 2008-10-06 13:41 UTC (permalink / raw)
To: qemu-devel
Gal Hammer wrote:
>> Apart from anything else, it's missing documentation.
>>
>
> I am aware of that. I'll document the work I've done so far and include
> the open issues that you (and others) wrote.
>
>
>> It looks like you have to start qemu with magic options on the remote
>> machine?
>>
>
> No, qemu should be started with "-usbremote host:port" command line
> option. This will start a listener which wait for incoming remote
> connections. The remote connection is established with the qemu-usbd
> application. The qemu-usbd is executed on the local machine, the one
> that the USB device is attached to.
>
I think it would be better to support a push instead of a pull model.
For instance, something like:
-usbdevice remote:host:port
That connected to something that was listening on the machine that had
the USB device. If you really wanted a server, I'd suggest a syntax like:
-usbdevice remote:host:port,server
So at a high level, what's functional now, and how much work is needed
to be completely functional? Is there anything that isn't going to work
because of fundamental issues?
Regards,
Anthony Liguori
> Gal.
>
>
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [Qemu-devel] [PATCH] USB over network
2008-10-06 13:41 ` Anthony Liguori
@ 2008-10-06 14:20 ` Gal Hammer
2008-10-06 15:24 ` Alexander Graf
0 siblings, 1 reply; 13+ messages in thread
From: Gal Hammer @ 2008-10-06 14:20 UTC (permalink / raw)
To: qemu-devel
> I think it would be better to support a push instead of a pull model.
> For instance, something like:
>
> -usbdevice remote:host:port
>
> That connected to something that was listening on the machine that had
> the USB device. If you really wanted a server, I'd suggest a syntax
> like:
>
> -usbdevice remote:host:port,server
Thanks for the idea. I'll consider it. Please note that there might be a
problem with firewalls or NAT when switching the direction from
vnc->qemu to qemu->vnc.
> So at a high level, what's functional now, and how much work is needed
> to be completely functional? Is there anything that isn't going to
> work
> because of fundamental issues?
It is functional and work with normal bulk transfers (for example DOK).
It should work with web cameras (isochronous) but I still need to test
it.
Gal.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 14:20 ` Gal Hammer
@ 2008-10-06 15:24 ` Alexander Graf
0 siblings, 0 replies; 13+ messages in thread
From: Alexander Graf @ 2008-10-06 15:24 UTC (permalink / raw)
To: qemu-devel; +Cc: gal
Gal Hammer wrote:
>> I think it would be better to support a push instead of a pull model.
>> For instance, something like:
>>
>> -usbdevice remote:host:port
>>
>> That connected to something that was listening on the machine that had
>> the USB device. If you really wanted a server, I'd suggest a syntax
>> like:
>>
>> -usbdevice remote:host:port,server
>>
>
> Thanks for the idea. I'll consider it. Please note that there might be a
> problem with firewalls or NAT when switching the direction from
> vnc->qemu to qemu->vnc.
>
If you're worried about VNC, why not integrate this into the VNC protocol?
Best case here would of course be to have it in VNC optionally.
Alex
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 11:43 [Qemu-devel] [PATCH] USB over network Gal Hammer
2008-10-06 12:09 ` Paul Brook
@ 2008-10-06 12:32 ` Daniel P. Berrange
2008-10-06 15:05 ` Blue Swirl
2 siblings, 0 replies; 13+ messages in thread
From: Daniel P. Berrange @ 2008-10-06 12:32 UTC (permalink / raw)
To: qemu-devel
On Mon, Oct 06, 2008 at 04:43:58AM -0700, Gal Hammer wrote:
> Hi,
>
> Attached is a preliminary patch which add QEmu the ability to use local
> USB devices over network. It should work with DOK devices and might work
> with web cameras.
AFAICT, the wire protocol doesn't provide for feature negotiation or
versioning, which somewhat limits our ability to extend its capabilities
in the future. This is a concern since it is also does not appear to
have any kind of security model in the existing impl - QEMU will
accept incoming client connections from anywhere. I'd like to see at
least an upfront feature negotiation/handshake, such that someone could
later add support for authentication and/or encryption, eg by layering
in GnuTLS and/or Kerberos via GSSAPI/SASL.
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-06 11:43 [Qemu-devel] [PATCH] USB over network Gal Hammer
2008-10-06 12:09 ` Paul Brook
2008-10-06 12:32 ` Daniel P. Berrange
@ 2008-10-06 15:05 ` Blue Swirl
2008-10-07 8:34 ` Gal Hammer
2 siblings, 1 reply; 13+ messages in thread
From: Blue Swirl @ 2008-10-06 15:05 UTC (permalink / raw)
To: qemu-devel
On 10/6/08, Gal Hammer <gal@qumranet.com> wrote:
> Attached is a preliminary patch which add QEmu the ability to use local USB
> devices over network. It should work with DOK devices and might work with
> web cameras.
How about USB drives or printers, do they work?
A few comments:
- 'static' can be applied to a lot of functions
- instead of including linux/usb_ch9.h, you should define the
structures, like in usb-linux.c
- while the daemon is linux specific, the remote protocol looks like
host OS neutral, so it should not be too tightly tied to usb-linux.c
- name __ioctl looks like it could clash with system defines and it's
not very descriptive
- USB_REMOTE_MAGIC is not endian neutral, or is that the purpose?
- upper-case structure names are IMHO ugly
Otherwise, great idea!
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [Qemu-devel] [PATCH] USB over network
2008-10-06 15:05 ` Blue Swirl
@ 2008-10-07 8:34 ` Gal Hammer
2008-10-07 18:08 ` Blue Swirl
0 siblings, 1 reply; 13+ messages in thread
From: Gal Hammer @ 2008-10-07 8:34 UTC (permalink / raw)
To: qemu-devel
> How about USB drives or printers, do they work?
Basically, if it works locally, it should work over network. The remote feature should be as good as the qemu's current support for USB emulation.
> A few comments:
> - 'static' can be applied to a lot of functions
I'll search for these.
> - instead of including linux/usb_ch9.h, you should define the
> structures, like in usb-linux.c
No problem.
> - while the daemon is linux specific, the remote protocol looks like
> host OS neutral, so it should not be too tightly tied to usb-linux.c
The protocol is still in its pre-draft status.
> - name __ioctl looks like it could clash with system defines and it's
> not very descriptive
Will changed (Do you have an idea how to name it?).
> - USB_REMOTE_MAGIC is not endian neutral, or is that the purpose?
I didn't understand. Do you mean that the value might change if qemu is running on a different type of CPU?
> - upper-case structure names are IMHO ugly
Can't take the Windows' coding conversion from a Windows programmer :-). I'll lower case them.
> Otherwise, great idea!
Thanks. But I can't take the credit for it.
Gal.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] USB over network
2008-10-07 8:34 ` Gal Hammer
@ 2008-10-07 18:08 ` Blue Swirl
0 siblings, 0 replies; 13+ messages in thread
From: Blue Swirl @ 2008-10-07 18:08 UTC (permalink / raw)
To: qemu-devel
On 10/7/08, Gal Hammer <gal@qumranet.com> wrote:
> > How about USB drives or printers, do they work?
>
>
> Basically, if it works locally, it should work over network. The remote feature should be as good as the qemu's current support for USB emulation.
>
>
> > A few comments:
> > - 'static' can be applied to a lot of functions
>
>
> I'll search for these.
>
>
> > - instead of including linux/usb_ch9.h, you should define the
> > structures, like in usb-linux.c
>
>
> No problem.
>
>
> > - while the daemon is linux specific, the remote protocol looks like
> > host OS neutral, so it should not be too tightly tied to usb-linux.c
>
>
> The protocol is still in its pre-draft status.
I meant that also Windows and BSD clients could in theory use a USB
device from the Linux-only server, so the client end of the protocol
should be independent from usb-linux.c.
> > - name __ioctl looks like it could clash with system defines and it's
> > not very descriptive
>
>
> Will changed (Do you have an idea how to name it?).
USBRemoteIOCTLFunc?
> > - USB_REMOTE_MAGIC is not endian neutral, or is that the purpose?
>
>
> I didn't understand. Do you mean that the value might change if qemu is running on a different type of CPU?
Yes. That would cause problems if the server is different endian from
the client. In fact the whole protocol looks like it also assumes that
the server and client are same endian.
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2008-10-07 18:08 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-06 11:43 [Qemu-devel] [PATCH] USB over network Gal Hammer
2008-10-06 12:09 ` Paul Brook
2008-10-06 12:22 ` Paul Brook
2008-10-06 12:42 ` Dor Laor
2008-10-06 13:15 ` Gal Hammer
2008-10-06 13:32 ` Paul Brook
2008-10-06 13:41 ` Anthony Liguori
2008-10-06 14:20 ` Gal Hammer
2008-10-06 15:24 ` Alexander Graf
2008-10-06 12:32 ` Daniel P. Berrange
2008-10-06 15:05 ` Blue Swirl
2008-10-07 8:34 ` Gal Hammer
2008-10-07 18:08 ` Blue Swirl
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).