*** hw/usb-tcp.c	Wed Dec 31 14:00:00 1969
+++ hw/usb-tcp.c	Fri Apr  6 23:36:01 2007
@@ -0,0 +1,590 @@
+/*
+ * QEMU USB over TCP protocol
+ * 
+ * Copyright (c) 2007 Eduardo Felipe
+ * 
+ * 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 "vl.h"
+#include "qemu_socket.h"
+
+//#define DEBUG_USBTCP
+
+#ifdef DEBUG_USBTCP
+#define updebug(fmt, arg...) printf("usb-tcp: " fmt, ##arg)
+#else
+#define updebug(fmt, arg...) ((void)0)
+#endif
+
+
+typedef struct USBTCPState USBTCPState;
+
+typedef struct Buffer
+{
+    size_t capacity;
+    size_t offset;
+    char *buffer;
+} Buffer;
+
+typedef int USBTCPReadEvent(USBTCPState *s, char *data, size_t len);
+
+struct USBTCPState {
+    USBDevice dev;
+    int lsock;
+    int csock;
+    int plugged;
+    
+    Buffer output;
+    Buffer input;
+    
+    USBTCPReadEvent *read_handler;
+    size_t read_handler_expect;
+    
+};
+
+/* XXX remove global variable to allow multiple instances */
+USBTCPState *ps;
+
+extern int parse_host_port(struct sockaddr_in *saddr, const char *str);
+
+static void usb_tcp_write(USBTCPState *s, const void *data, size_t len);
+static void usb_tcp_write_s32(USBTCPState *s, int32_t value);
+static void usb_tcp_write_u32(USBTCPState *s, uint32_t value);
+static void usb_tcp_write_u16(USBTCPState *s, uint16_t value);
+static void usb_tcp_write_u8(USBTCPState *s, uint8_t value);
+static void usb_tcp_flush(USBTCPState *s);
+static int usb_tcp_client_io_error(USBTCPState *s, int ret, int last_errno);
+static void usb_tcp_client_error(USBTCPState *s);
+static void usb_tcp_client_write(void *opaque);
+static void usb_tcp_client_read(void *opaque);
+
+static uint8_t read_u8(uint8_t *data, size_t offset);
+static uint16_t read_u16(uint8_t *data, size_t offset);
+static int16_t read_s16(uint8_t *data, size_t offset);
+static int32_t read_s32(uint8_t *data, size_t offset);
+static uint32_t read_u32(uint8_t *data, size_t offset);
+
+static void usb_tcp_listen_read(void *opaque);
+static int usb_tcp_listen_poll(void *opaque);
+
+
+static void buffer_reserve(Buffer *buffer, size_t len)
+{
+    if ((buffer->capacity - buffer->offset) < len) {
+        buffer->capacity += (len + 1024);
+        buffer->buffer = realloc(buffer->buffer, buffer->capacity);
+        if (buffer->buffer == NULL) {
+            fprintf(stderr, "usb-tcp: out of memory\n");
+            exit(1);
+        }
+    }
+}
+
+static void buffer_consume(Buffer *buffer,size_t len)
+{
+    memmove(buffer->buffer, buffer->buffer + len, (buffer->offset - len));
+    buffer->offset -= len;
+}
+
+
+static int buffer_empty(Buffer *buffer)
+{
+    return buffer->offset == 0;
+}
+
+static char *buffer_end(Buffer *buffer)
+{
+    return buffer->buffer + buffer->offset;
+}
+
+static void buffer_reset(Buffer *buffer)
+{
+        buffer->offset = 0;
+}
+
+static void buffer_append(Buffer *buffer, const void *data, size_t len)
+{
+    memcpy(buffer->buffer + buffer->offset, data, len);
+    buffer->offset += len;
+}
+
+static int usb_tcp_client_io_error(USBTCPState *s, int ret, int last_errno)
+{
+    char delstr[6];
+    
+    if (ret == 0 || ret == -1) {
+        if (ret == -1 && (last_errno == EINTR || last_errno == EAGAIN))
+            return 0;
+
+        // Detach device
+        if (s->plugged) {
+            s->plugged = 0;
+            sprintf(delstr,"%d.%d", 0, s->dev.addr);
+            do_usb_del(delstr);
+        }
+
+        // Close connection
+        if (s->csock!=-1) {
+            qemu_set_fd_handler2(s->csock, NULL, NULL, NULL, NULL);
+            closesocket(s->csock);
+            s->csock = -1;
+            buffer_reset(&s->input);
+            buffer_reset(&s->output);    
+        }
+        return 0;
+    }
+    return ret;
+}
+
+static void usb_tcp_client_error(USBTCPState *s)
+{
+    usb_tcp_client_io_error(s, -1, EINVAL);
+}
+
+static void usb_tcp_client_write(void *opaque)
+{
+    long ret;
+    USBTCPState *s = opaque;
+
+    ret = send(s->csock, s->output.buffer, s->output.offset, 0);
+    ret = usb_tcp_client_io_error(s, ret, socket_error());
+    if (!ret)
+        return;
+
+    memmove(s->output.buffer, s->output.buffer + ret, (s->output.offset - ret));
+    s->output.offset -= ret;
+
+    if (s->output.offset == 0) {
+        qemu_set_fd_handler2(s->csock, NULL, usb_tcp_client_read, NULL, s);
+    }
+}
+
+
+static void usb_tcp_read_when(USBTCPState *s, USBTCPReadEvent *func, size_t expecting)
+{
+    s->read_handler = func;
+    s->read_handler_expect = expecting;
+}
+
+
+static int usb_tcp_read_all(void *opaque)
+{
+    USBTCPState *s = opaque;
+    int ret;
+    
+    buffer_reserve(&s->input, 4096);
+
+    do {
+        ret = recv(s->csock, buffer_end(&s->input), 4096, 0);
+    } while (ret==-1 && socket_error()==EINTR);
+
+    ret = usb_tcp_client_io_error(s, ret, socket_error());
+
+    s->input.offset += ret;
+    
+    return ret;
+}
+
+static void usb_tcp_client_read(void *opaque)
+{
+    USBTCPState *s = opaque;
+    int ret;
+    
+    buffer_reserve(&s->input, 4096);
+
+    ret = recv(s->csock, buffer_end(&s->input), 4096, 0);
+    
+    ret = usb_tcp_client_io_error(s, ret, socket_error());
+    if (!ret)
+        return;
+
+    s->input.offset += ret;
+
+    while (s->read_handler && s->input.offset >= s->read_handler_expect) {
+
+        int ret;
+        size_t len = s->read_handler_expect;
+
+        ret = s->read_handler(s, s->input.buffer, len);
+        if (s->csock == -1)
+            return;
+        
+        if (!ret) {
+            buffer_consume(&s->input,len);
+        } else {
+            s->read_handler_expect = ret;
+        }
+    }
+}
+
+
+static void usb_tcp_write(USBTCPState *s, const void *data, size_t len)
+{
+    buffer_reserve(&s->output, len);
+    
+    if (buffer_empty(&s->output)) {
+        qemu_set_fd_handler2(s->csock, NULL, usb_tcp_client_read, NULL, s);
+    }
+
+    buffer_append(&s->output, data, len);
+}
+
+static void usb_tcp_write_s32(USBTCPState *s, int32_t value)
+{
+    usb_tcp_write_u32(s, *(uint32_t *)&value);
+}
+
+static void usb_tcp_write_u32(USBTCPState *s, uint32_t value)
+{
+    uint8_t buf[4];
+
+    buf[0] = (value >> 24) & 0xFF;
+    buf[1] = (value >> 16) & 0xFF;
+    buf[2] = (value >>  8) & 0xFF;
+    buf[3] = value & 0xFF;
+
+    usb_tcp_write(s, buf, 4);
+}
+
+static void usb_tcp_write_u16(USBTCPState *s, uint16_t value)
+{
+    uint8_t buf[2];
+
+    buf[0] = (value >> 8) & 0xFF;
+    buf[1] = value & 0xFF;
+
+    usb_tcp_write(s, buf, 2);
+}
+
+static void usb_tcp_write_u8(USBTCPState *s, uint8_t value)
+{
+    usb_tcp_write(s, (char *)&value, 1);
+}
+
+static void usb_tcp_flush(USBTCPState *s)
+{
+    if (s->output.offset)
+        usb_tcp_client_write(s);
+}
+
+static uint8_t read_u8(uint8_t *data, size_t offset)
+{
+    uint8_t *ptr = (uint8_t *)data;
+    return ptr[offset];
+}
+
+static uint16_t read_u16(uint8_t *data, size_t offset)
+{
+    uint8_t *ptr = (uint8_t *)data;
+    return ((ptr[offset] & 0xFF) << 8) | (ptr[offset + 1] & 0xFF);
+}
+
+static int16_t read_s16(uint8_t *data, size_t offset)
+{
+    uint8_t *ptr = (uint8_t *)data;
+    return (int16_t)((ptr[offset] & 0xFF) << 8) | (ptr[offset + 1] & 0xFF);
+}
+
+static int32_t read_s32(uint8_t *data, size_t offset)
+{
+    uint8_t *ptr = (uint8_t *)data;
+    return (int32_t)((ptr[offset] << 24) | (ptr[offset + 1] << 16) |
+                     (ptr[offset + 2] << 8) | ptr[offset + 3]);
+}
+
+static uint32_t read_u32(uint8_t *data, size_t offset)
+{
+    uint8_t *ptr = (uint8_t *)data;
+    return ((ptr[offset] << 24) | (ptr[offset + 1] << 16) |
+            (ptr[offset + 2] << 8) | ptr[offset + 3]);
+}
+
+
+static void usb_tcp_handle_reset(USBDevice *dev)
+{
+    USBTCPState *s = (USBTCPState *)dev;
+    int avlen;
+    int16_t datalen;
+
+    if (s->csock == -1)
+        return USB_RET_STALL;
+    
+    updebug("[Reset]\n");
+    usb_tcp_write(s,"RS",2);
+    usb_tcp_flush(s);
+
+    avlen = usb_tcp_read_all(s);
+    datalen = read_s16(s->input.buffer,0);
+    if (datalen>=0)
+        buffer_consume(&s->input,datalen+2);
+}
+
+static int usb_tcp_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    USBTCPState *s = (USBTCPState *)dev;
+    int ret = 0;
+    int avlen;
+    int16_t datalen;
+    
+    updebug("[Control]  request:0x%04x  value:0x%04x\n",request,value);
+    
+    if (s->csock == -1)
+        return USB_RET_STALL;
+    
+    // QEMU side handling
+    switch(request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP)
+            dev->remote_wakeup = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP)
+            dev->remote_wakeup = 1;
+        break;
+    // ...
+    }
+    
+    usb_tcp_write(s,"CT",2);
+    usb_tcp_write_u16(s, request); 
+    usb_tcp_write_u16(s, value); 
+    usb_tcp_write_u16(s, index); 
+    usb_tcp_write_u16(s, length); 
+    usb_tcp_flush(s);
+
+    avlen = usb_tcp_read_all(s);
+    if (avlen>=2) {
+        datalen = read_s16(s->input.buffer,0);
+
+        if (datalen>0)
+            memcpy(data,s->input.buffer+2,datalen);
+
+        if (datalen>=0)
+            buffer_consume(&s->input,datalen+2);
+        
+        return datalen;
+    }
+    else {
+        return USB_RET_STALL;
+    }
+}
+
+
+static int usb_tcp_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBTCPState *s = (USBTCPState *)dev;
+    int ret = 0;
+    int avlen;
+    int16_t datalen;
+    
+    updebug("[Data]  pid:0x%02x  packet size:%d\n",p->pid,p->len);
+    
+    if (s->csock == -1)
+        return USB_RET_STALL;
+    
+    switch(p->pid) {
+    case USB_TOKEN_IN:
+        if (p->devep != 1)
+            goto fail;
+            
+        usb_tcp_write(s,"DI",2);
+        usb_tcp_write_u16(s, p->len); 
+        usb_tcp_flush(s);
+        avlen = usb_tcp_read_all(s);
+        
+        if (avlen < 2)
+            goto fail;
+        
+        datalen = read_s16(s->input.buffer,0);
+
+        if (datalen>0)
+            memcpy(p->data,s->input.buffer+2,datalen);
+
+        if (datalen>=0)
+            buffer_consume(&s->input,datalen+2);
+        
+        ret = datalen;
+        break;
+    case USB_TOKEN_OUT:
+        /* XXX TODO */
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_tcp_handle_destroy(USBDevice *dev)
+{
+    USBTCPState *s = (USBTCPState *)dev;
+    updebug("[Destroy]\n");
+    
+    usb_tcp_client_error(s);
+    memset(&s->dev, 0, sizeof(USBDevice));
+}
+
+static int protocol_version(USBTCPState *s, char *version, size_t len)
+{
+    char local[15];
+    int maj, min;
+
+    memcpy(local, version, 15);
+    local[15] = 0;
+
+    if (sscanf(local, "USBTCP %03d.%03d\n", &maj, &min) != 2) {
+        usb_tcp_client_error(s);
+        return 0;
+    }    
+
+    updebug("new USB device plugged\n");
+    do_usb_add("TCPdevice");
+    
+    s->plugged = 1;
+    
+    qemu_set_fd_handler2(s->csock, NULL, NULL, NULL, NULL);
+}
+
+static void usb_tcp_listen_read(void *opaque)
+{
+    USBTCPState *s = opaque;
+    struct sockaddr_in addr;
+    socklen_t addrlen = sizeof(addr);
+
+    s->csock = accept(s->lsock, (struct sockaddr *)&addr, &addrlen);
+    if (s->csock != -1) {
+        qemu_set_fd_handler2(s->csock, NULL, usb_tcp_client_read, NULL, opaque);
+        
+        usb_tcp_write(s,"USBTCP 000.001\n",15);
+        usb_tcp_flush(s);
+        usb_tcp_read_when(s, protocol_version, 15);
+    }
+}
+
+
+static int usb_tcp_listen_poll(void *opaque)
+{
+    USBTCPState *s = opaque;
+    if (s->csock == -1)
+        return 1;
+    return 0;
+}
+
+
+
+USBDevice *usb_tcp_device_init(void)
+{
+    USBDevice *d;
+    
+    d = &ps->dev;
+
+    d->speed = USB_SPEED_FULL;
+    d->handle_packet = usb_generic_handle_packet;
+    d->handle_reset = usb_tcp_handle_reset;
+    d->handle_control = usb_tcp_handle_control;
+    d->handle_data = usb_tcp_handle_data;
+    d->handle_destroy = usb_tcp_handle_destroy;
+
+    pstrcpy(d->devname, sizeof(d->devname), "QEMU USB over TCP");
+    
+    return (USBDevice *)ps;
+}
+
+
+
+void usb_tcp_init(const char *arg)
+{
+    struct sockaddr *addr;
+    struct sockaddr_in iaddr;
+#ifndef _WIN32
+    struct sockaddr_un uaddr;
+#endif
+    int reuse_addr, ret;
+    socklen_t addrlen;
+    const char *p;
+    
+    ps = qemu_mallocz(sizeof(USBTCPState));
+    if (!ps)
+        return;
+    
+    ps->lsock = -1;
+    ps->csock = -1;
+    
+#ifndef _WIN32
+    if (strstart(arg, "unix:", &p)) {
+    addr = (struct sockaddr *)&uaddr;
+    addrlen = sizeof(uaddr);
+
+    ps->lsock = socket(PF_UNIX, SOCK_STREAM, 0);
+    if (ps->lsock == -1) {
+        fprintf(stderr, "usb-tcp: Could not create socket\n");
+        exit(1);
+    }
+
+    uaddr.sun_family = AF_UNIX;
+    memset(uaddr.sun_path, 0, 108);
+    snprintf(uaddr.sun_path, 108, "%s", p);
+
+    unlink(uaddr.sun_path);
+    } else
+#endif
+    {
+    addr = (struct sockaddr *)&iaddr;
+    addrlen = sizeof(iaddr);
+
+    ps->lsock = socket(PF_INET, SOCK_STREAM, 0);
+    if (ps->lsock == -1) {
+        fprintf(stderr, "usb-tcp: Could not create socket\n");
+        exit(1);
+    }
+
+    if (parse_host_port(&iaddr, arg) < 0) {
+        fprintf(stderr, "usb-tcp: Could not parse address\n");
+        exit(1);
+    }
+
+    reuse_addr = 1;
+    ret = setsockopt(ps->lsock, SOL_SOCKET, SO_REUSEADDR,
+                     (const char *)&reuse_addr, sizeof(reuse_addr));
+    if (ret == -1) {
+        fprintf(stderr, "usb-tcp: setsockopt() failed\n");
+        exit(1);
+    }
+    }
+
+    if (bind(ps->lsock, addr, addrlen) == -1) {
+        fprintf(stderr, "usb-tcp: bind() failed\n");
+        exit(1);
+    }
+
+    if (listen(ps->lsock, 1) == -1) {
+        fprintf(stderr, "usb-tcp: listen() failed\n");
+        exit(1);
+    }
+
+    ret = qemu_set_fd_handler2(ps->lsock, usb_tcp_listen_poll, usb_tcp_listen_read, NULL, ps);
+    if (ret == -1) {
+        exit(1);
+    }
+}
+
+

*** Makefile.target	6 Apr 2007 16:49:48 -0000	1.158
--- Makefile.target	6 Apr 2007 22:16:31 -0000
@@ -397,7 +397,7 @@
 VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
 
 # USB layer
-VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o
+VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o usb-tcp.o
 
 # EEPROM emulation
 VL_OBJS += eeprom93xx.o

*** vl.c	6 Apr 2007 16:49:48 -0000	1.279
--- vl.c	6 Apr 2007 22:16:32 -0000
@@ -173,6 +173,7 @@
 int win2k_install_hack = 0;
 #endif
 int usb_enabled = 0;
+const char *usbtcp_port;
 static VLANState *first_vlan;
 int smp_cpus = 1;
 const char *vnc_display;
@@ -4293,6 +4294,8 @@
 	dev = usb_tablet_init();
     } else if (strstart(devname, "disk:", &p)) {
         dev = usb_msd_init(p);
+    } else if (!strcmp(devname, "TCPdevice")) {
+        dev = usb_tcp_device_init();
     } else {
         return -1;
     }
@@ -6376,6 +6379,7 @@
 #endif
            "-usb            enable the USB driver (will be the default soon)\n"
            "-usbdevice name add the host or guest USB device 'name'\n"
+           "-usbtcp port    start a USB over TCP server on port\n"
 #if defined(TARGET_PPC) || defined(TARGET_SPARC)
            "-g WxH[xDEPTH]  Set the initial graphical resolution and depth\n"
 #endif
@@ -6534,6 +6538,7 @@
     QEMU_OPTION_win2k_hack,
     QEMU_OPTION_usb,
     QEMU_OPTION_usbdevice,
+    QEMU_OPTION_usbtcp,
     QEMU_OPTION_smp,
     QEMU_OPTION_vnc,
     QEMU_OPTION_no_acpi,
@@ -6620,6 +6625,7 @@
     { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
     { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
     { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
+    { "usbtcp", HAS_ARG, QEMU_OPTION_usbtcp },
     { "smp", HAS_ARG, QEMU_OPTION_smp },
     { "vnc", HAS_ARG, QEMU_OPTION_vnc },
 
@@ -7319,6 +7325,10 @@
                         optarg);
                 usb_devices_index++;
                 break;
+            case QEMU_OPTION_usbtcp:
+                usb_enabled = 1;
+                usbtcp_port = optarg;
+                break;
             case QEMU_OPTION_smp:
                 smp_cpus = atoi(optarg);
                 if (smp_cpus < 1 || smp_cpus > MAX_CPUS) {
@@ -7637,6 +7647,8 @@
                         usb_devices[i]);
             }
         }
+        if (usbtcp_port != NULL)
+            usb_tcp_init(usbtcp_port);
     }
 
     gui_timer = qemu_new_timer(rt_clock, gui_update, NULL);

*** hw/usb.h	17 Mar 2007 16:59:30 -0000	1.12
--- hw/usb.h	5 Apr 2007 13:53:35 -0000
@@ -220,3 +220,7 @@
 
 /* usb-msd.c */
 USBDevice *usb_msd_init(const char *filename);
+
+/* usb-tcp.c */
+void usb_tcp_init(const char *arg);
+USBDevice *usb_tcp_device_init(void);

