[Spice-commits] Branch 'usb.2.wip' - 9 commits - hw/hw.h hw/usb-audio.c hw/usb-bus.c hw/usb-ehci.c hw/usb-hid.c hw/usb-hub.c hw/usb-msd.c hw/usb-musb.c hw/usb-ohci.c hw/usb-uhci.c hw/usb.h

Gerd Hoffmann kraxel at kemper.freedesktop.org
Fri Dec 10 06:41:25 PST 2010


 hw/hw.h        |   10 +++
 hw/usb-audio.c |  173 ++++++++++++++++++++++++++++++++++++++++++++++++------
 hw/usb-bus.c   |   64 +++++++++++++++++---
 hw/usb-ehci.c  |  182 +++++++++++++++++++++++++--------------------------------
 hw/usb-hid.c   |   55 +++++++++++++++--
 hw/usb-hub.c   |   11 +++
 hw/usb-msd.c   |   16 ++++-
 hw/usb-musb.c  |    1 
 hw/usb-ohci.c  |    1 
 hw/usb-uhci.c  |    1 
 hw/usb.h       |    7 +-
 11 files changed, 387 insertions(+), 134 deletions(-)

New commits:
commit e38db000acf2316c04bca8ff264b1b0ea677c0e8
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 14:58:41 2010 +0100

    [wip] usb storage: add migration support
    
    ... works at least when no request is in flight ...
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 729d96c..d47d3f9 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -592,10 +592,22 @@ static USBDevice *usb_msd_init(const char *filename)
     return dev;
 }
 
+static const VMStateDescription vmstate_usb_msd = {
+    .name = "usb-msd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, MSDState),
+        /* TODO: handle transactions which are in flight */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static struct USBDeviceInfo msd_info = {
     .product_desc   = "QEMU USB MSD",
     .qdev.name      = "usb-storage",
     .qdev.size      = sizeof(MSDState),
+    .qdev.vmsd      = &vmstate_usb_msd,
     .usb_desc       = &desc,
     .init           = usb_msd_initfn,
     .handle_packet  = usb_generic_handle_packet,
commit b0a64fcd57275a2e779304cf7c127e0f585ab7e0
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 14:40:30 2010 +0100

    [wip] usb hid: add migration support
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 60fa57f..0108687 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -46,8 +46,8 @@
 #define USB_KEYBOARD  3
 
 typedef struct USBMouseState {
-    int dx, dy, dz, buttons_state;
-    int x, y;
+    int32_t dx, dy, dz, buttons_state;
+    int32_t x, y;
     int mouse_grabbed;
     QEMUPutMouseEntry *eh_entry;
 } USBMouseState;
@@ -56,7 +56,7 @@ typedef struct USBKeyboardState {
     uint16_t modifiers;
     uint8_t leds;
     uint8_t key[16];
-    int keys;
+    int32_t keys;
 } USBKeyboardState;
 
 typedef struct USBHIDState {
@@ -66,7 +66,7 @@ typedef struct USBHIDState {
         USBKeyboardState kbd;
     };
     int kind;
-    int protocol;
+    int32_t protocol;
     uint8_t idle;
     int64_t next_idle_clock;
     int changed;
@@ -835,12 +835,57 @@ void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *))
     s->datain = datain;
 }
 
+static int usb_hid_post_load(void *opaque, int version_id)
+{
+    USBHIDState *s = opaque;
+
+    if (s->idle) {
+        usb_hid_set_next_idle(s, qemu_get_clock(vm_clock));
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_ptr = {
+    .name = "usb-ptr",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = usb_hid_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, USBHIDState),
+        VMSTATE_INT32(ptr.dx, USBHIDState),
+        VMSTATE_INT32(ptr.dy, USBHIDState),
+        VMSTATE_INT32(ptr.buttons_state, USBHIDState),
+        VMSTATE_INT32(ptr.x, USBHIDState),
+        VMSTATE_INT32(ptr.y, USBHIDState),
+        VMSTATE_UINT8(idle, USBHIDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_usb_kbd = {
+    .name = "usb-kbd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = usb_hid_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_USB_DEVICE(dev, USBHIDState),
+        VMSTATE_UINT16(kbd.modifiers, USBHIDState),
+        VMSTATE_UINT8(kbd.leds, USBHIDState),
+        VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16),
+        VMSTATE_INT32(kbd.keys, USBHIDState),
+        VMSTATE_INT32(protocol, USBHIDState),
+        VMSTATE_UINT8(idle, USBHIDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static struct USBDeviceInfo hid_info[] = {
     {
         .product_desc   = "QEMU USB Tablet",
         .qdev.name      = "usb-tablet",
         .usbdevice_name = "tablet",
         .qdev.size      = sizeof(USBHIDState),
+        .qdev.vmsd      = &vmstate_usb_ptr,
         .usb_desc       = &desc_tablet,
         .init           = usb_tablet_initfn,
         .handle_packet  = usb_generic_handle_packet,
@@ -853,6 +898,7 @@ static struct USBDeviceInfo hid_info[] = {
         .qdev.name      = "usb-mouse",
         .usbdevice_name = "mouse",
         .qdev.size      = sizeof(USBHIDState),
+        .qdev.vmsd      = &vmstate_usb_ptr,
         .usb_desc       = &desc_mouse,
         .init           = usb_mouse_initfn,
         .handle_packet  = usb_generic_handle_packet,
@@ -865,6 +911,7 @@ static struct USBDeviceInfo hid_info[] = {
         .qdev.name      = "usb-kbd",
         .usbdevice_name = "keyboard",
         .qdev.size      = sizeof(USBHIDState),
+        .qdev.vmsd      = &vmstate_usb_kbd,
         .usb_desc       = &desc_keyboard,
         .init           = usb_keyboard_initfn,
         .handle_packet  = usb_generic_handle_packet,
commit 5d9c193f15b05f6dc3264d26267532c488cab2f7
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 14:20:46 2010 +0100

    [wip] usb core: add migration support
    
    Yes, seriously.  There is no migration support at all for usb devices.
    After migration devices just stop responding because they loose state,
    especially the device address.  Luckily there is so much broken usb
    hardware out there that the guest just kicks the device hard (via port
    reset and reinitialization) and goes on.  So we got away with that for
    at least some guest/device combinations.
    
    Lets start fixing that.  Add a vmstate struct for USBDevice.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/hw.h b/hw/hw.h
index 77bfb58..a757277 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -576,6 +576,16 @@ extern const VMStateDescription vmstate_i2c_slave;
     .offset     = vmstate_offset_value(_state, _field, i2c_slave),   \
 }
 
+extern const VMStateDescription vmstate_usb_device;
+
+#define VMSTATE_USB_DEVICE(_field, _state) {                         \
+    .name       = (stringify(_field)),                               \
+    .size       = sizeof(USBDevice),                                 \
+    .vmsd       = &vmstate_usb_device,                               \
+    .flags      = VMS_STRUCT,                                        \
+    .offset     = vmstate_offset_value(_state, _field, USBDevice),   \
+}
+
 #define vmstate_offset_macaddr(_state, _field)                       \
     vmstate_offset_array(_state, _field.a, uint8_t,                \
                          sizeof(typeof_field(_state, _field)))
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index b0ddf04..ddb7e63 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -20,6 +20,19 @@ static struct BusInfo usb_bus_info = {
 static int next_usb_bus = 0;
 static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
 
+const VMStateDescription vmstate_usb_device = {
+    .name = "USBDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT8(addr, USBDevice),
+        VMSTATE_INT32(state, USBDevice),
+        VMSTATE_INT32(remote_wakeup, USBDevice),
+        /* TODO: handle transactions which are in flight */
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
 void usb_bus_new(USBBus *bus, DeviceState *host)
 {
     qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
diff --git a/hw/usb.h b/hw/usb.h
index 08b8b85..bd536b2 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -172,10 +172,10 @@ struct USBDevice {
     int auto_attach;
     int attached;
 
-    int state;
+    int32_t state;
     uint8_t setup_buf[8];
     uint8_t data_buf[1024];
-    int remote_wakeup;
+    int32_t remote_wakeup;
     int setup_state;
     int setup_len;
     int setup_index;
commit dfa79fddeeb8e671f9ea0848b97e0809adba1272
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 11:43:35 2010 +0100

    usb: add port property.
    
    This allows to explictily set the physical port where you want to
    plug the usb device.  Example:
    
      -device usb-tablet,bus=usb.0,port=2
    
    With explicit port addressing qemu can and will not automagically add
    USB Hubs.  This means that:
    
      (a) You can plug two devices of your choice into the two uhci
          root ports.
      (b) If you want plug in more that two devices you have to care
          about adding a hub yourself.
    
    Plugging a hub works this way:
    
      -device usb-hub,bus=usb.0,port=1
    
    Use this to add a device to the hub:
    
      -device usb-tablet,bus=usb.0,port=1.1
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index cfed593..b0ddf04 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -12,6 +12,10 @@ static struct BusInfo usb_bus_info = {
     .size      = sizeof(USBBus),
     .print_dev = usb_bus_dev_print,
     .get_dev_path = usb_get_dev_path,
+    .props      = (Property[]) {
+        DEFINE_PROP_STRING("port", USBDevice, port_path),
+        DEFINE_PROP_END_OF_LIST()
+    },
 };
 static int next_usb_bus = 0;
 static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
@@ -151,9 +155,22 @@ static void do_attach(USBDevice *dev)
                 dev->product_desc);
         return;
     }
-    dev->attached++;
+    if (dev->port_path) {
+        QTAILQ_FOREACH(port, &bus->free, next) {
+            if (strcmp(port->path, dev->port_path) == 0) {
+                break;
+            }
+        }
+        if (port == NULL) {
+            fprintf(stderr, "Warning: usb port %s (bus %s) not found\n",
+                    dev->port_path, bus->qbus.name);
+            return;
+        }
+    } else {
+        port = QTAILQ_FIRST(&bus->free);
+    }
 
-    port = QTAILQ_FIRST(&bus->free);
+    dev->attached++;
     QTAILQ_REMOVE(&bus->free, port, next);
     bus->nfree--;
 
@@ -167,8 +184,9 @@ int usb_device_attach(USBDevice *dev)
 {
     USBBus *bus = usb_bus_from_device(dev);
 
-    if (bus->nfree == 1) {
-        /* Create a new hub and chain it on.  */
+    if (bus->nfree == 1 && dev->port_path == NULL) {
+        /* Create a new hub and chain it on
+           (unless a physical port location is specified). */
         usb_create_simple(bus, "usb-hub");
     }
     do_attach(dev);
diff --git a/hw/usb.h b/hw/usb.h
index 3126e10..08b8b85 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -163,6 +163,7 @@ struct USBDevice {
     DeviceState qdev;
     USBDeviceInfo *info;
     USBPort *port;
+    char *port_path;
     void *opaque;
 
     int speed;
commit b1bbff9d6028df39594bc726dcf8858594d6bf88
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 11:37:45 2010 +0100

    usb: keep track of physical port address.
    
    Add a path string to USBPort.  Add usb_port_location() function to set
    the physical location of the usb port.  Update all drivers implementing
    usb ports to call it.  Update the monitor commands to print it.  Wind it
    up in qdev.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index 9772e1e..cfed593 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -5,11 +5,13 @@
 #include "monitor.h"
 
 static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
+static char *usb_get_dev_path(DeviceState *dev);
 
 static struct BusInfo usb_bus_info = {
     .name      = "USB",
     .size      = sizeof(USBBus),
     .print_dev = usb_bus_dev_print,
+    .get_dev_path = usb_get_dev_path,
 };
 static int next_usb_bus = 0;
 static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
@@ -121,6 +123,16 @@ void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
     bus->nfree++;
 }
 
+void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
+{
+    if (upstream) {
+        snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
+                 upstream->path, portnr);
+    } else {
+        snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
+    }
+}
+
 void usb_unregister_port(USBBus *bus, USBPort *port)
 {
     if (port->dev)
@@ -230,12 +242,19 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
     USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
     USBBus *bus = usb_bus_from_device(dev);
 
-    monitor_printf(mon, "%*saddr %d.%d, speed %s, name %s%s\n",
+    monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
                    indent, "", bus->busnr, dev->addr,
+                   dev->port ? dev->port->path : "-",
                    usb_speed(dev->speed), dev->product_desc,
                    dev->attached ? ", attached" : "");
 }
 
+static char *usb_get_dev_path(DeviceState *qdev)
+{
+    USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+    return qemu_strdup(dev->port->path);
+}
+
 void usb_info(Monitor *mon)
 {
     USBBus *bus;
@@ -252,8 +271,8 @@ void usb_info(Monitor *mon)
             dev = port->dev;
             if (!dev)
                 continue;
-            monitor_printf(mon, "  Device %d.%d, Speed %s Mb/s, Product %s\n",
-                           bus->busnr, dev->addr, usb_speed(dev->speed),
+            monitor_printf(mon, "  Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
+                           bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
                            dev->product_desc);
         }
     }
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index a9dbd54..3a49353 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -2010,6 +2010,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
     for(i = 0; i < NB_PORTS; i++) {
         usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
                           USB_SPEED_MASK_HIGH);
+        usb_port_location(&s->ports[i], NULL, i+1);
         s->ports[i].dev = 0;
     }
 
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 652a9d5..25ede61 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -245,6 +245,16 @@ static void usb_hub_detach(USBPort *port1)
     }
 }
 
+static void usb_hub_handle_attach(USBDevice *dev)
+{
+    USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+    int i;
+
+    for (i = 0; i < NUM_PORTS; i++) {
+        usb_port_location(&s->ports[i].port, dev->port, i+1);
+    }
+}
+
 static void usb_hub_handle_reset(USBDevice *dev)
 {
     /* XXX: do it */
@@ -529,6 +539,7 @@ static struct USBDeviceInfo hub_info = {
     .usb_desc       = &desc_hub,
     .init           = usb_hub_initfn,
     .handle_packet  = usb_hub_handle_packet,
+    .handle_attach  = usb_hub_handle_attach,
     .handle_reset   = usb_hub_handle_reset,
     .handle_control = usb_hub_handle_control,
     .handle_data    = usb_hub_handle_data,
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
index ac7b684..f7cb9a7 100644
--- a/hw/usb-musb.c
+++ b/hw/usb-musb.c
@@ -351,6 +351,7 @@ struct MUSBState {
     usb_bus_new(&s->bus, NULL /* FIXME */);
     usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
                       USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+    usb_port_location(&s->port, NULL, 1);
 
     return s;
 }
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 32f5f69..771c7cf 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -1707,6 +1707,7 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
     for (i = 0; i < num_ports; i++) {
         usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops,
                           USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+        usb_port_location(&ohci->rhport[i].port, NULL, i+1);
     }
 
     ohci->async_td = 0;
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 6786df7..2a86d14 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -1147,6 +1147,7 @@ static int usb_uhci_common_initfn(UHCIState *s)
     for(i = 0; i < NB_PORTS; i++) {
         usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
                           USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+        usb_port_location(&s->ports[i].port, NULL, i+1);
     }
     s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s);
     s->expire_time = qemu_get_clock(vm_clock) +
diff --git a/hw/usb.h b/hw/usb.h
index 623bbdc..3126e10 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -247,6 +247,7 @@ typedef struct USBPortOps {
 struct USBPort {
     USBDevice *dev;
     int speedmask;
+    char path[16];
     USBPortOps *ops;
     void *opaque;
     int index; /* internal port index, may be used with the opaque */
@@ -360,6 +361,7 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name);
 USBDevice *usbdevice_create(const char *cmdline);
 void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
                        USBPortOps *ops, int speedmask);
+void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr);
 void usb_unregister_port(USBBus *bus, USBPort *port);
 int usb_device_attach(USBDevice *dev);
 int usb_device_detach(USBDevice *dev);
commit 158b7f6fff081aef17a2bf125bcce62c486bcd29
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 11:06:47 2010 +0100

    switch usb audio to desc [incomplete]

diff --git a/hw/usb-audio.c b/hw/usb-audio.c
index 8fb00b4..3673b9e 100644
--- a/hw/usb-audio.c
+++ b/hw/usb-audio.c
@@ -25,6 +25,7 @@
 
 #include "qemu-common.h"
 #include "usb.h"
+#include "usb-desc.h"
 #include "hw.h"
 #include "audiodev.h"
 #include "audio/audio.h"
@@ -59,7 +60,7 @@ enum usb_audio_strings {
     STRING_REAL_STREAM,
 };
 
-static const char * const usb_audio_stringtable[256] = {
+static const USBDescStrings usb_audio_stringtable = {
     [STRING_MANUFACTURER]	= "QEMU",
     [STRING_PRODUCT]		= "QEMU USB Audio",
     [STRING_SERIALNUMBER]	= "1",
@@ -184,23 +185,6 @@ static const uint8_t qemu_usb_audio_config_descriptor[] = {
     USB_SUBCLASS_AUDIO_STREAMING, /* u8 bInterfaceSubclass */
     0x00,			/*  u8  bInterfaceProtocol */
     STRING_REAL_STREAM,		/*  u8  iInterface */
-    /* Headphone Class-specific AS General Interface Descriptor */
-    0x07,			/*  u8  bLength */
-    USB_DT_CS_INTERFACE,	/*  u8  bDescriptorType */
-    DST_AS_GENERAL,		/*  u8  bDescriptorSubtype */
-    0x01,			/*  u8  bTerminalLink */
-    0x00,			/*  u8  bDelay */
-    0x01, 0x00,			/* u16  wFormatTag */
-    /* Headphone Type I Format Type Descriptor */
-    0x0b,			/*  u8  bLength */
-    USB_DT_CS_INTERFACE,	/*  u8  bDescriptorType */
-    DST_AS_FORMAT_TYPE,		/*  u8  bDescriptorSubtype */
-    0x01,			/*  u8  bFormatType */
-    0x02,			/*  u8  bNrChannels */
-    0x02,			/*  u8  bSubFrameSize */
-    0x10,			/*  u8  bBitResolution */
-    0x01,			/*  u8  bSamFreqType */
-    U24(USBAUDIO_SAMPLE_RATE),	/* u24  tSamFreq */
     /* Stereo Headphone Standard AS Audio Data Endpoint Descriptor */
     0x09,			/*  u8  bLength */
     USB_DT_ENDPOINT,		/*  u8  bDescriptorType */
@@ -219,6 +203,151 @@ static const uint8_t qemu_usb_audio_config_descriptor[] = {
     U16(0x0000),		/* u16  wLockDelay */
 };
 
+static const USBDescIface desc_iface[] = {
+    {
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_CONTROL,
+        .bInterfaceProtocol            = 0x04,
+        .iInterface                    = STRING_USBAUDIO_CONTROL,
+        .ndesc                         = 4,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-Specific AC Interface Header Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
+                    U16(0x0100),                /* u16  bcdADC */
+                    U16(0x2b),                  /* u16  wTotalLength */
+                    0x01,                       /*  u8  bInCollection */
+                    0x01,                       /*  u8  baInterfaceNr */
+                }
+            },{
+                /* Generic Stereo Input Terminal ID1 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0c,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalID */
+                    U16(0x0101),                /* u16  wTerminalType */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /* u16  bNrChannels */
+                    U16(0x0003),                /* u16  wChannelConfig */
+                    0x00,                       /*  u8  iChannelNames */
+                    STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
+                }
+            },{
+                /* Generic Stereo Feature Unit ID2 Descriptor */
+                .data = (uint8_t[]) {
+                    0x0d,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
+                    0x02,                       /*  u8  bUnitID */
+                    0x01,                       /*  u8  bSourceID */
+                    0x02,                       /*  u8  bControlSize */
+                    U16(0x0001),                /* u16  bmaControls(0) */
+                    U16(0x0002),                /* u16  bmaControls(1) */
+                    U16(0x0002),                /* u16  bmaControls(2) */
+                    STRING_FEATURE_UNIT,        /*  u8  iFeature */
+                }
+            },{
+                /* Headphone Ouptut Terminal ID3 Descriptor */
+                .data = (uint8_t[]) {
+                    0x09,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
+                    0x03,                       /*  u8  bUnitID */
+                    U16(0x0301),                /* u16  wTerminalType (SPEAKER) */
+                    0x00,                       /*  u8  bAssocTerminal */
+                    0x02,                       /*  u8  bSourceID */
+                    STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
+                }
+            }
+        },
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 0,
+        .bNumEndpoints                 = 0,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_NULL_STREAM,
+    },{
+        .bInterfaceNumber              = 1,
+        .bAlternateSetting             = 1,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = USB_CLASS_AUDIO,
+        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
+        .iInterface                    = STRING_REAL_STREAM,
+        .ndesc                         = 2,
+        .descs = (USBDescOther[]) {
+            {
+                /* Headphone Class-specific AS General Interface Descriptor */
+                .data = (uint8_t[]) {
+                    0x07,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bTerminalLink */
+                    0x00,                       /*  u8  bDelay */
+                    0x01, 0x00,                 /* u16  wFormatTag */
+                }
+            },{
+                /* Headphone Type I Format Type Descriptor */
+                .data = (uint8_t[]) {
+                    0x0b,                       /*  u8  bLength */
+                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+                    0x01,                       /*  u8  bFormatType */
+                    0x02,                       /*  u8  bNrChannels */
+                    0x02,                       /*  u8  bSubFrameSize */
+                    0x10,                       /*  u8  bBitResolution */
+                    0x01,                       /*  u8  bSamFreqType */
+                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+                }
+            }
+        },
+        .eps = (USBDescEndpoint[]) {
+            {
+                .bEndpointAddress      = USB_DIR_OUT | 0x01,
+                .bmAttributes          = 0x0d,
+                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE,
+                .bInterval             = 1,
+            },
+        }
+    }
+};
+
+static const USBDescDevice desc_device = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = DEV_CONFIG_VALUE,
+            .iConfiguration        = STRING_CONFIG,
+            .bmAttributes          = 0xc0,
+            .bMaxPower             = 0x32,
+            .nif = ARRAY_SIZE(desc_iface),
+            .ifs = desc_iface,
+        },
+    },
+};
+
+static const USBDesc desc_audio = {
+    .id = {
+        .idVendor          = USBAUDIO_VENDOR_NUM,
+        .idProduct         = USBAUDIO_PRODUCT_NUM,
+        .bcdDevice         = 0,
+        .iManufacturer     = STRING_MANUFACTURER,
+        .iProduct          = STRING_PRODUCT,
+        .iSerialNumber     = STRING_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = usb_audio_stringtable,
+};
+
 /*
  * A USB audio device supports an arbitrary number of alternate
  * interface settings for each interface.  Each corresponds to a block
@@ -478,6 +607,11 @@ static int usb_audio_handle_control(USBDevice *dev, int request, int value,
                 "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
                 request, value, index, length);
 
+    ret = usb_desc_handle_control(dev, request, value, index, length, data);
+    if (ret >= 0) {
+        return ret;
+    }
+
     switch(request) {
     case DeviceRequest | USB_REQ_GET_STATUS:
         data[0] = (1 << USB_DEVICE_SELF_POWERED) |
@@ -711,7 +845,7 @@ static int usb_audio_initfn(USBDevice *dev)
 {
     USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
 
-    s->dev.speed  = USB_SPEED_FULL;
+    usb_desc_init(dev);
     s->dev.opaque = s;
     AUD_register_card("usb-audio", &s->card);
 
@@ -737,6 +871,7 @@ static struct USBDeviceInfo usb_audio_info = {
     .usbdevice_name = "audio",
     .qdev.name      = "usb-audio",
     .qdev.size      = sizeof(USBAudioState),
+    .usb_desc       = &desc_audio,
     .init           = usb_audio_initfn,
     .handle_packet  = usb_generic_handle_packet,
     .handle_reset   = usb_audio_handle_reset,
commit bee1f4369e39888c41ea2930b6e5be6a288418ba
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Dec 10 00:11:05 2010 +0100

    ehci: zap EHCI_NOMICROFRAMES
    
    Trying to emulate 8000 Hz microframe timing is hopeless anyway,
    even 1000 Hz frame rate is hard enougth.  Don't even try.

diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 6bd1c52..a9dbd54 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -141,13 +141,7 @@
 #define PORTSC_CSC           (1 << 1)     // Connect Status Change
 #define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
 
-//#define EHCI_NOMICROFRAMES
-
-#ifdef EHCI_NOMICROFRAMES
 #define FRAME_TIMER_FREQ 1000
-#else
-#define FRAME_TIMER_FREQ 8000
-#endif
 #define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ)
 
 #define NB_MAXINTRATE    8        // Max rate at which controller issues ints
@@ -1116,111 +1110,103 @@ static int ehci_process_itd(EHCIState *ehci,
     endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
     maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
 
-#ifdef EHCI_NOMICROFRAMES
     for(i = 0; i < 8; i++) {
-#else
-    i =(ehci->frindex & 7);
-#endif
-
-    if (itd->transact[i] & ITD_XACT_ACTIVE) {
-        DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
-                ehci->frindex >> 3, i);
+        if (itd->transact[i] & ITD_XACT_ACTIVE) {
+            DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
+                    ehci->frindex >> 3, i);
 
-        pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
-        ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
-                  (itd->transact[i] & ITD_XACT_OFFSET_MASK);
-        len = get_field(itd->transact[i], ITD_XACT_LENGTH);
+            pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
+            ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
+                (itd->transact[i] & ITD_XACT_OFFSET_MASK);
+            len = get_field(itd->transact[i], ITD_XACT_LENGTH);
 
-        if (len > BUFF_SIZE) {
-            return USB_RET_PROCERR;
-        }
+            if (len > BUFF_SIZE) {
+                return USB_RET_PROCERR;
+            }
 
-        DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
+            DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
 
-        if (!dir) {
-            cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0);
-            pid = USB_TOKEN_OUT;
-        } else
-            pid = USB_TOKEN_IN;
+            if (!dir) {
+                cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0);
+                pid = USB_TOKEN_OUT;
+            } else
+                pid = USB_TOKEN_IN;
 
-        ret = USB_RET_NODEV;
+            ret = USB_RET_NODEV;
 
-        for (j = 0; j < NB_PORTS; j++) {
-            port = &ehci->ports[j];
-            dev = port->dev;
+            for (j = 0; j < NB_PORTS; j++) {
+                port = &ehci->ports[j];
+                dev = port->dev;
 
-            // TODO sometime we will also need to check if we are the port owner
+                // TODO sometime we will also need to check if we are the port owner
 
-            if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
-                DPRINTF("Port %d, no exec, not connected(%08X)\n",
-                        j, ehci->portsc[j]);
-                continue;
-            }
+                if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
+                    DPRINTF("Port %d, no exec, not connected(%08X)\n",
+                            j, ehci->portsc[j]);
+                    continue;
+                }
 
-            ehci->usb_packet.pid = ehci->pid;
-            ehci->usb_packet.devaddr = devadr;
-            ehci->usb_packet.devep = endp;
-            ehci->usb_packet.data = ehci->buffer;
-            ehci->usb_packet.len = len;
-            ehci->usb_packet.complete_cb = ehci_async_complete_packet;
-            ehci->usb_packet.complete_opaque = ehci;
+                ehci->usb_packet.pid = ehci->pid;
+                ehci->usb_packet.devaddr = devadr;
+                ehci->usb_packet.devep = endp;
+                ehci->usb_packet.data = ehci->buffer;
+                ehci->usb_packet.len = len;
+                ehci->usb_packet.complete_cb = ehci_async_complete_packet;
+                ehci->usb_packet.complete_opaque = ehci;
 
-            DPRINTF("calling dev->info->handle_packet\n");
-            ret = dev->info->handle_packet(dev, &ehci->usb_packet);
+                DPRINTF("calling dev->info->handle_packet\n");
+                ret = dev->info->handle_packet(dev, &ehci->usb_packet);
 
-            if (ret != USB_RET_NODEV) {
-                break;
+                if (ret != USB_RET_NODEV) {
+                    break;
+                }
             }
-        }
 
-        /*  In isoch, there is no facility to indicate a NAK so let's
-         *  instead just complete a zero-byte transaction.  Setting
-         *  DBERR seems too draconian.
-         */
+            /*  In isoch, there is no facility to indicate a NAK so let's
+             *  instead just complete a zero-byte transaction.  Setting
+             *  DBERR seems too draconian.
+             */
 
-        if (ret == USB_RET_NAK) {
-            if (ehci->isoch_pause > 0) {
-                DPRINTF("ISOCH: received a NAK but paused so returning\n");
-                ehci->isoch_pause--;
-                return 0;
-            } else if (ehci->isoch_pause == -1) {
-                DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
-                // Pause frindex for up to 50 msec waiting for data from
-                // remote
-                ehci->isoch_pause = 50;
-                return 0;
+            if (ret == USB_RET_NAK) {
+                if (ehci->isoch_pause > 0) {
+                    DPRINTF("ISOCH: received a NAK but paused so returning\n");
+                    ehci->isoch_pause--;
+                    return 0;
+                } else if (ehci->isoch_pause == -1) {
+                    DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
+                    // Pause frindex for up to 50 msec waiting for data from
+                    // remote
+                    ehci->isoch_pause = 50;
+                    return 0;
+                } else {
+                    DPRINTF("ISOCH: isoch pause timeout! return 0\n");
+                    ret = 0;
+                }
             } else {
-                DPRINTF("ISOCH: isoch pause timeout! return 0\n");
-                ret = 0;
+                DPRINTF("ISOCH: received ACK, clearing pause\n");
+                ehci->isoch_pause = -1;
             }
-        } else {
-            DPRINTF("ISOCH: received ACK, clearing pause\n");
-            ehci->isoch_pause = -1;
-        }
 
-        if (ret >= 0) {
-            itd->transact[i] &= ~ITD_XACT_ACTIVE;
+            if (ret >= 0) {
+                itd->transact[i] &= ~ITD_XACT_ACTIVE;
 
-            if (itd->transact[i] & ITD_XACT_IOC) {
-                // TODO should do this after writeback to memory
-                ehci_set_interrupt(ehci, USBSTS_INT);
+                if (itd->transact[i] & ITD_XACT_IOC) {
+                    // TODO should do this after writeback to memory
+                    ehci_set_interrupt(ehci, USBSTS_INT);
+                }
             }
-        }
 
-        if (ret >= 0 && dir) {
-            cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1);
+            if (ret >= 0 && dir) {
+                cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1);
 
-            if (ret != len) {
-                DPRINTF("ISOCH IN expected %d, got %d\n",
-                        len, ret);
-                set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
+                if (ret != len) {
+                    DPRINTF("ISOCH IN expected %d, got %d\n",
+                            len, ret);
+                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
+                }
             }
         }
     }
-
-#ifdef EHCI_NOMICROFRAMES
-    }
-#endif
     return 0;
 }
 
@@ -1516,16 +1502,6 @@ static int ehci_state_execute(EHCIState *ehci, int async, int *state)
     }
 
     smask = get_field(qh->epcap, QH_EPCAP_SMASK);
-#ifndef EHCI_NOMICROFRAMES
-    if (smask && (smask & (1 << (ehci->frindex & 7))) == 0) {
-        DPRINTF_ST("PERIODIC active not interval: mask %x, frindex %d,%d\n",
-                   smask, (ehci->frindex >> 3),(ehci->frindex & 7));
-
-        *state = EST_HORIZONTALQH;
-        again = 1;
-        goto out;
-    }
-#endif
 
     if (!smask) {
         reload = get_field(qh->epchar, QH_EPCHAR_RL);
@@ -1887,11 +1863,7 @@ static void ehci_frame_timer(void *opaque)
     for (i = 0; i < frames; i++) {
         if ( !(ehci->usbsts & USBSTS_HALT)) {
             if (ehci->isoch_pause <= 0) {
-#ifdef EHCI_NOMICROFRAMES
                 ehci->frindex += 8;
-#else
-                ehci->frindex++;
-#endif
             }
 
             if (ehci->frindex > 0x00001fff) {
commit af04f3aea45ee77ee4ea7cc67f0953723a3ae8cf
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Dec 9 23:31:49 2010 +0100

    usb storage: handle long responses
    
    The scsi layer may return us more data than the guests wants to have.
    Handle this by just ignoring the extra bytes and calling the
    {read,write}_data callback to finish the request.
    
    Seen happening in real life with some extended inquiry command.
    With this patch applied the linux kernel stops reseting the device
    once at boot.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 28c12dd..729d96c 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -187,7 +187,7 @@ static void usb_msd_copy_data(MSDState *s)
     s->usb_buf += len;
     s->scsi_buf += len;
     s->data_len -= len;
-    if (s->scsi_len == 0) {
+    if (s->scsi_len == 0 || s->data_len == 0) {
         if (s->mode == USB_MSDM_DATAIN) {
             s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
         } else if (s->mode == USB_MSDM_DATAOUT) {
@@ -434,7 +434,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             break;
 
         case USB_MSDM_DATAIN:
-            DPRINTF("Data in %d/%d\n", len, s->data_len);
+            DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len);
             if (len > s->data_len)
                 len = s->data_len;
             s->usb_buf = data;
commit 06dccfe506b1709ed0798ee95ed25f2a4040ea1e
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Dec 9 22:43:25 2010 +0100

    ehci: add list of authors

diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 850ffbe..6bd1c52 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -3,6 +3,15 @@
  *
  * Copyright(c) 2008  Emutex Ltd. (address at hidden)
  *
+ * Authors & Contributors:
+ *    Mark Burkley
+ *    Niels de Vos
+ *    David S. Ahern
+ *    Kevin Wolf
+ *    Jan Kiszka
+ *    Vincent Palatin
+ *    Gerd Hoffmann
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either


More information about the Spice-commits mailing list