From 3f1f2961c65891ad35c6928e113be21b9d0a9779 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Jul 19 2011 09:49:04 +0000 Subject: Add support usb redirection over the network, see: http://fedoraproject.org/wiki/Features/UsbNetworkRedirection Restore chardev flow control patches --- diff --git a/0001-hw-usb-musb.c-Don-t-misuse-usb_packet_complete.patch b/0001-hw-usb-musb.c-Don-t-misuse-usb_packet_complete.patch new file mode 100644 index 0000000..135f851 --- /dev/null +++ b/0001-hw-usb-musb.c-Don-t-misuse-usb_packet_complete.patch @@ -0,0 +1,32 @@ +From 5b1a8791870cbb83cce780be54da6029be4126a4 Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Tue, 14 Jun 2011 12:24:04 +0100 +Subject: [PATCH 01/35] hw/usb-musb.c: Don't misuse usb_packet_complete() + +In musb_packet() handle final processing of non-asynchronous +USB packets by directly calling musb_schedule_cb() rather than +going through usb_packet_complete(). The latter will trigger +an assertion because the packet doesn't belong to a device. + +Signed-off-by: Peter Maydell +Signed-off-by: Gerd Hoffmann +--- + hw/usb-musb.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/hw/usb-musb.c b/hw/usb-musb.c +index 21f35afa..d15971f 100644 +--- a/hw/usb-musb.c ++++ b/hw/usb-musb.c +@@ -616,7 +616,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, + } + + ep->status[dir] = ret; +- usb_packet_complete(s->port.dev, &ep->packey[dir].p); ++ musb_schedule_cb(s->port.dev, &ep->packey[dir].p); + } + + static void musb_tx_packet_complete(USBPacket *packey, void *opaque) +-- +1.7.5.1 + diff --git a/0002-usb-Add-a-usb_fill_port-helper-function.patch b/0002-usb-Add-a-usb_fill_port-helper-function.patch new file mode 100644 index 0000000..43ea3cf --- /dev/null +++ b/0002-usb-Add-a-usb_fill_port-helper-function.patch @@ -0,0 +1,42 @@ +From dc1a2be79b202d353d320393445ccd9db6263371 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 30 Jun 2011 11:57:57 +0200 +Subject: [PATCH 02/35] usb: Add a usb_fill_port helper function + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-bus.c | 10 ++++++++-- + 1 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/hw/usb-bus.c b/hw/usb-bus.c +index 2abce12..776974e 100644 +--- a/hw/usb-bus.c ++++ b/hw/usb-bus.c +@@ -140,8 +140,8 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name) + return dev; + } + +-void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, +- USBPortOps *ops, int speedmask) ++static void usb_fill_port(USBPort *port, void *opaque, int index, ++ USBPortOps *ops, int speedmask) + { + port->opaque = opaque; + port->index = index; +@@ -149,6 +149,12 @@ void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + port->index = index; + port->ops = ops; + port->speedmask = speedmask; ++} ++ ++void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, ++ USBPortOps *ops, int speedmask) ++{ ++ usb_fill_port(port, opaque, index, ops, speedmask); + QTAILQ_INSERT_TAIL(&bus->free, port, next); + bus->nfree++; + } +-- +1.7.5.1 + diff --git a/0003-usb-Move-initial-call-of-usb_port_location-to-usb_fi.patch b/0003-usb-Move-initial-call-of-usb_port_location-to-usb_fi.patch new file mode 100644 index 0000000..cba122d --- /dev/null +++ b/0003-usb-Move-initial-call-of-usb_port_location-to-usb_fi.patch @@ -0,0 +1,83 @@ +From 397fed3bc9bf6dd0e8e18c5be077897299e5c4e2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 30 Jun 2011 12:05:19 +0200 +Subject: [PATCH 03/35] usb: Move (initial) call of usb_port_location to + usb_fill_port + +Cleanup / preparation patch for companion controller support. Note that +as a "side-effect" this patch also fixes the milkymist-softusb controller +not having a port_location set for its ports. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-bus.c | 1 + + hw/usb-ehci.c | 1 - + hw/usb-musb.c | 1 - + hw/usb-ohci.c | 1 - + hw/usb-uhci.c | 1 - + 5 files changed, 1 insertions(+), 4 deletions(-) + +diff --git a/hw/usb-bus.c b/hw/usb-bus.c +index 776974e..e37e8a2 100644 +--- a/hw/usb-bus.c ++++ b/hw/usb-bus.c +@@ -149,6 +149,7 @@ static void usb_fill_port(USBPort *port, void *opaque, int index, + port->index = index; + port->ops = ops; + port->speedmask = speedmask; ++ usb_port_location(port, NULL, index + 1); + } + + void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 91fb7de..88cb2c2 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -2206,7 +2206,6 @@ 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-musb.c b/hw/usb-musb.c +index d15971f..84e6017 100644 +--- a/hw/usb-musb.c ++++ b/hw/usb-musb.c +@@ -369,7 +369,6 @@ struct MUSBState *musb_init(qemu_irq *irqs) + usb_bus_new(&s->bus, &musb_bus_ops, 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 1c29b9f..95e4623 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -1742,7 +1742,6 @@ 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 405fa7b..fd25d2a 100644 +--- a/hw/usb-uhci.c ++++ b/hw/usb-uhci.c +@@ -1129,7 +1129,6 @@ static int usb_uhci_common_initfn(PCIDevice *dev) + 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_ns(vm_clock, uhci_frame_timer, s); + s->num_ports_vmstate = NB_PORTS; +-- +1.7.5.1 + diff --git a/0004-usb-Add-a-register_companion-USB-bus-op.patch b/0004-usb-Add-a-register_companion-USB-bus-op.patch new file mode 100644 index 0000000..3cdd193 --- /dev/null +++ b/0004-usb-Add-a-register_companion-USB-bus-op.patch @@ -0,0 +1,82 @@ +From fc63639374684dae600d200c133adad75044e587 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 11:29:56 +0200 +Subject: [PATCH 04/35] usb: Add a register_companion USB bus op. + +This is a preparation patch for adding support for USB companion controllers. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-bus.c | 31 +++++++++++++++++++++++++++++++ + hw/usb.h | 5 +++++ + 2 files changed, 36 insertions(+), 0 deletions(-) + +diff --git a/hw/usb-bus.c b/hw/usb-bus.c +index e37e8a2..b511bac 100644 +--- a/hw/usb-bus.c ++++ b/hw/usb-bus.c +@@ -160,6 +160,37 @@ void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + bus->nfree++; + } + ++int usb_register_companion(const char *masterbus, USBPort *ports[], ++ uint32_t portcount, uint32_t firstport, ++ void *opaque, USBPortOps *ops, int speedmask) ++{ ++ USBBus *bus; ++ int i; ++ ++ QTAILQ_FOREACH(bus, &busses, next) { ++ if (strcmp(bus->qbus.name, masterbus) == 0) { ++ break; ++ } ++ } ++ ++ if (!bus || !bus->ops->register_companion) { ++ qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", ++ "an USB masterbus"); ++ if (bus) { ++ error_printf_unless_qmp( ++ "USB bus '%s' does not allow companion controllers\n", ++ masterbus); ++ } ++ return -1; ++ } ++ ++ for (i = 0; i < portcount; i++) { ++ usb_fill_port(ports[i], opaque, i, ops, speedmask); ++ } ++ ++ return bus->ops->register_companion(bus, ports, portcount, firstport); ++} ++ + void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) + { + if (upstream) { +diff --git a/hw/usb.h b/hw/usb.h +index 076e2ff..a5f2efa 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -344,6 +344,8 @@ struct USBBus { + }; + + struct USBBusOps { ++ int (*register_companion)(USBBus *bus, USBPort *ports[], ++ uint32_t portcount, uint32_t firstport); + void (*device_destroy)(USBBus *bus, USBDevice *dev); + }; + +@@ -356,6 +358,9 @@ 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); ++int usb_register_companion(const char *masterbus, USBPort *ports[], ++ uint32_t portcount, uint32_t firstport, ++ void *opaque, 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); +-- +1.7.5.1 + diff --git a/0005-usb-Make-port-wakeup-and-complete-ops-take-a-USBPort.patch b/0005-usb-Make-port-wakeup-and-complete-ops-take-a-USBPort.patch new file mode 100644 index 0000000..9a30d18 --- /dev/null +++ b/0005-usb-Make-port-wakeup-and-complete-ops-take-a-USBPort.patch @@ -0,0 +1,213 @@ +From 99a493bf96aa03427633b24653112b43fa7b7131 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 21 Jun 2011 11:52:28 +0200 +Subject: [PATCH 05/35] usb: Make port wakeup and complete ops take a USBPort + instead of a Device + +This makes them consistent with the attach and detach ops, and in general +it makes sense to make portops take a port as argument. This also makes +adding support for a companion controller easier / cleaner. + +[ kraxel: fix usb-musb.c build ] + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 2 +- + hw/usb-hub.c | 10 +++++----- + hw/usb-musb.c | 6 +++--- + hw/usb-ohci.c | 12 +++++------- + hw/usb-uhci.c | 11 +++++------ + hw/usb.c | 4 ++-- + hw/usb.h | 9 +++++++-- + 7 files changed, 28 insertions(+), 26 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 88cb2c2..428c90b 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1111,7 +1111,7 @@ static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) + return 0; + } + +-static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet) ++static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) + { + EHCIQueue *q = container_of(packet, EHCIQueue, packet); + +diff --git a/hw/usb-hub.c b/hw/usb-hub.c +index 6e2a358..d324bba 100644 +--- a/hw/usb-hub.c ++++ b/hw/usb-hub.c +@@ -246,10 +246,10 @@ static void usb_hub_detach(USBPort *port1) + } + } + +-static void usb_hub_wakeup(USBDevice *dev) ++static void usb_hub_wakeup(USBPort *port1) + { +- USBHubState *s = dev->port->opaque; +- USBHubPort *port = &s->ports[dev->port->index]; ++ USBHubState *s = port1->opaque; ++ USBHubPort *port = &s->ports[port1->index]; + + if (port->wPortStatus & PORT_STAT_SUSPEND) { + port->wPortChange |= PORT_STAT_C_SUSPEND; +@@ -257,9 +257,9 @@ static void usb_hub_wakeup(USBDevice *dev) + } + } + +-static void usb_hub_complete(USBDevice *dev, USBPacket *packet) ++static void usb_hub_complete(USBPort *port, USBPacket *packet) + { +- USBHubState *s = dev->port->opaque; ++ USBHubState *s = port->opaque; + + /* + * Just pass it along upstream for now. +diff --git a/hw/usb-musb.c b/hw/usb-musb.c +index 84e6017..580bdc8 100644 +--- a/hw/usb-musb.c ++++ b/hw/usb-musb.c +@@ -261,7 +261,7 @@ + + static void musb_attach(USBPort *port); + static void musb_detach(USBPort *port); +-static void musb_schedule_cb(USBDevice *dev, USBPacket *p); ++static void musb_schedule_cb(USBPort *port, USBPacket *p); + static void musb_device_destroy(USBBus *bus, USBDevice *dev); + + static USBPortOps musb_port_ops = { +@@ -517,7 +517,7 @@ static void musb_cb_tick1(void *opaque) + + #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) + +-static void musb_schedule_cb(USBDevice *dev, USBPacket *packey) ++static void musb_schedule_cb(USBPort *port, USBPacket *packey) + { + MUSBPacket *p = container_of(packey, MUSBPacket, p); + MUSBEndPoint *ep = p->ep; +@@ -615,7 +615,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, + } + + ep->status[dir] = ret; +- musb_schedule_cb(s->port.dev, &ep->packey[dir].p); ++ musb_schedule_cb(&s->port, &ep->packey[dir].p); + } + + static void musb_tx_packet_complete(USBPacket *packey, void *opaque) +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index 95e4623..bd92c31 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -367,15 +367,13 @@ static void ohci_detach(USBPort *port1) + ohci_set_interrupt(s, OHCI_INTR_RHSC); + } + +-static void ohci_wakeup(USBDevice *dev) ++static void ohci_wakeup(USBPort *port1) + { +- USBBus *bus = usb_bus_from_device(dev); +- OHCIState *s = container_of(bus, OHCIState, bus); +- int portnum = dev->port->index; +- OHCIPort *port = &s->rhport[portnum]; ++ OHCIState *s = port1->opaque; ++ OHCIPort *port = &s->rhport[port1->index]; + uint32_t intr = 0; + if (port->ctrl & OHCI_PORT_PSS) { +- DPRINTF("usb-ohci: port %d: wakeup\n", portnum); ++ DPRINTF("usb-ohci: port %d: wakeup\n", port1->index); + port->ctrl |= OHCI_PORT_PSSC; + port->ctrl &= ~OHCI_PORT_PSS; + intr = OHCI_INTR_RHSC; +@@ -602,7 +600,7 @@ static void ohci_copy_iso_td(OHCIState *ohci, + + static void ohci_process_lists(OHCIState *ohci, int completion); + +-static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet) ++static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) + { + OHCIState *ohci = container_of(packet, OHCIState, usb_packet); + #ifdef DEBUG_PACKET +diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c +index fd25d2a..ab635f6 100644 +--- a/hw/usb-uhci.c ++++ b/hw/usb-uhci.c +@@ -620,11 +620,10 @@ static void uhci_detach(USBPort *port1) + uhci_resume(s); + } + +-static void uhci_wakeup(USBDevice *dev) ++static void uhci_wakeup(USBPort *port1) + { +- USBBus *bus = usb_bus_from_device(dev); +- UHCIState *s = container_of(bus, UHCIState, bus); +- UHCIPort *port = s->ports + dev->port->index; ++ UHCIState *s = port1->opaque; ++ UHCIPort *port = &s->ports[port1->index]; + + if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { + port->ctrl |= UHCI_PORT_RD; +@@ -657,7 +656,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) + return ret; + } + +-static void uhci_async_complete(USBDevice *dev, USBPacket *packet); ++static void uhci_async_complete(USBPort *port, USBPacket *packet); + static void uhci_process_frame(UHCIState *s); + + /* return -1 if fatal error (frame must be stopped) +@@ -849,7 +848,7 @@ done: + return len; + } + +-static void uhci_async_complete(USBDevice *dev, USBPacket *packet) ++static void uhci_async_complete(USBPort *port, USBPacket *packet) + { + UHCIAsync *async = container_of(packet, UHCIAsync, packet); + UHCIState *s = async->uhci; +diff --git a/hw/usb.c b/hw/usb.c +index 4a39cbc..735ffd1 100644 +--- a/hw/usb.c ++++ b/hw/usb.c +@@ -52,7 +52,7 @@ void usb_attach(USBPort *port, USBDevice *dev) + void usb_wakeup(USBDevice *dev) + { + if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { +- dev->port->ops->wakeup(dev); ++ dev->port->ops->wakeup(dev->port); + } + } + +@@ -335,7 +335,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) + { + /* Note: p->owner != dev is possible in case dev is a hub */ + assert(p->owner != NULL); +- dev->port->ops->complete(dev, p); ++ dev->port->ops->complete(dev->port, p); + p->owner = NULL; + } + +diff --git a/hw/usb.h b/hw/usb.h +index a5f2efa..65f45a0 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -252,8 +252,13 @@ struct USBDeviceInfo { + typedef struct USBPortOps { + void (*attach)(USBPort *port); + void (*detach)(USBPort *port); +- void (*wakeup)(USBDevice *dev); +- void (*complete)(USBDevice *dev, USBPacket *p); ++ void (*wakeup)(USBPort *port); ++ /* ++ * Note that port->dev will be different then the device from which ++ * the packet originated when a hub is involved, if you want the orginating ++ * device use p->owner ++ */ ++ void (*complete)(USBPort *port, USBPacket *p); + } USBPortOps; + + /* USB port on which a device can be connected */ +-- +1.7.5.1 + diff --git a/0006-usb-Replace-device_destroy-bus-op-with-a-child_detac.patch b/0006-usb-Replace-device_destroy-bus-op-with-a-child_detac.patch new file mode 100644 index 0000000..5811b96 --- /dev/null +++ b/0006-usb-Replace-device_destroy-bus-op-with-a-child_detac.patch @@ -0,0 +1,358 @@ +From a0f20940be744556be844ac857fa6dd679dc7af0 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 12:31:11 +0200 +Subject: [PATCH 06/35] usb: Replace device_destroy bus op with a child_detach + port op + +Note this fixes 2 things in one go, first of all the device_destroy bus +op should be a device_detach bus op, as pending async packets from the +device should be cancelled on detach not on destroy. + +Secondly having this as a bus op won't work with companion controllers, since +then there will be 1 bus driven by the ehci controller and thus 1 set of bus +ops, but the device being detached may be downstream of a handed over port. +Making the detach of a downstream device a port op allows the ehci controller +to forward this to the companion controller port for handed over ports. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/milkymist-softusb.c | 9 +++++++-- + hw/usb-bus.c | 2 -- + hw/usb-ehci.c | 18 ++++++++++-------- + hw/usb-hub.c | 12 ++++++++++++ + hw/usb-musb.c | 17 +++++++++++++---- + hw/usb-ohci.c | 16 ++++++++++++---- + hw/usb-uhci.c | 18 ++++++++++-------- + hw/usb.h | 6 +++++- + 8 files changed, 69 insertions(+), 29 deletions(-) + +diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c +index 5ab35c3..ce2bfc6 100644 +--- a/hw/milkymist-softusb.c ++++ b/hw/milkymist-softusb.c +@@ -247,16 +247,21 @@ static void softusb_attach(USBPort *port) + { + } + +-static void softusb_device_destroy(USBBus *bus, USBDevice *dev) ++static void softusb_detach(USBPort *port) ++{ ++} ++ ++static void softusb_child_detach(USBPort *port, USBDevice *child) + { + } + + static USBPortOps softusb_ops = { + .attach = softusb_attach, ++ .detach = softusb_detach, ++ .child_detach = softusb_child_detach, + }; + + static USBBusOps softusb_bus_ops = { +- .device_destroy = softusb_device_destroy, + }; + + static void milkymist_softusb_reset(DeviceState *d) +diff --git a/hw/usb-bus.c b/hw/usb-bus.c +index b511bac..c8347e9 100644 +--- a/hw/usb-bus.c ++++ b/hw/usb-bus.c +@@ -82,12 +82,10 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) + static int usb_qdev_exit(DeviceState *qdev) + { + USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); +- USBBus *bus = usb_bus_from_device(dev); + + if (dev->attached) { + usb_device_detach(dev); + } +- bus->ops->device_destroy(bus, dev); + if (dev->info->handle_destroy) { + dev->info->handle_destroy(dev); + } +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 428c90b..96451f3 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -751,6 +751,8 @@ static void ehci_detach(USBPort *port) + + trace_usb_ehci_port_detach(port->index); + ++ ehci_queues_rip_device(s, port->dev); ++ + *portsc &= ~PORTSC_CONNECT; + *portsc |= PORTSC_CSC; + +@@ -764,6 +766,13 @@ static void ehci_detach(USBPort *port) + } + } + ++static void ehci_child_detach(USBPort *port, USBDevice *child) ++{ ++ EHCIState *s = port->opaque; ++ ++ ehci_queues_rip_device(s, child); ++} ++ + /* 4.1 host controller initialization */ + static void ehci_reset(void *opaque) + { +@@ -2117,23 +2126,16 @@ static void ehci_map(PCIDevice *pci_dev, int region_num, + cpu_register_physical_memory(addr, size, s->mem); + } + +-static void ehci_device_destroy(USBBus *bus, USBDevice *dev) +-{ +- EHCIState *s = container_of(bus, EHCIState, bus); +- +- ehci_queues_rip_device(s, dev); +-} +- + static int usb_ehci_initfn(PCIDevice *dev); + + static USBPortOps ehci_port_ops = { + .attach = ehci_attach, + .detach = ehci_detach, ++ .child_detach = ehci_child_detach, + .complete = ehci_async_complete_packet, + }; + + static USBBusOps ehci_bus_ops = { +- .device_destroy = ehci_device_destroy, + }; + + static PCIDeviceInfo ehci_info = { +diff --git a/hw/usb-hub.c b/hw/usb-hub.c +index d324bba..b7557ce 100644 +--- a/hw/usb-hub.c ++++ b/hw/usb-hub.c +@@ -238,6 +238,9 @@ static void usb_hub_detach(USBPort *port1) + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; + ++ /* Let upstream know the device on this port is gone */ ++ s->dev.port->ops->child_detach(s->dev.port, port1->dev); ++ + port->wPortStatus &= ~PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->wPortStatus & PORT_STAT_ENABLE) { +@@ -246,6 +249,14 @@ static void usb_hub_detach(USBPort *port1) + } + } + ++static void usb_hub_child_detach(USBPort *port1, USBDevice *child) ++{ ++ USBHubState *s = port1->opaque; ++ ++ /* Pass along upstream */ ++ s->dev.port->ops->child_detach(s->dev.port, child); ++} ++ + static void usb_hub_wakeup(USBPort *port1) + { + USBHubState *s = port1->opaque; +@@ -537,6 +548,7 @@ static void usb_hub_handle_destroy(USBDevice *dev) + static USBPortOps usb_hub_port_ops = { + .attach = usb_hub_attach, + .detach = usb_hub_detach, ++ .child_detach = usb_hub_child_detach, + .wakeup = usb_hub_wakeup, + .complete = usb_hub_complete, + }; +diff --git a/hw/usb-musb.c b/hw/usb-musb.c +index 580bdc8..035dda8 100644 +--- a/hw/usb-musb.c ++++ b/hw/usb-musb.c +@@ -261,17 +261,18 @@ + + static void musb_attach(USBPort *port); + static void musb_detach(USBPort *port); ++static void musb_child_detach(USBPort *port, USBDevice *child); + static void musb_schedule_cb(USBPort *port, USBPacket *p); +-static void musb_device_destroy(USBBus *bus, USBDevice *dev); ++static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); + + static USBPortOps musb_port_ops = { + .attach = musb_attach, + .detach = musb_detach, ++ .child_detach = musb_child_detach, + .complete = musb_schedule_cb, + }; + + static USBBusOps musb_bus_ops = { +- .device_destroy = musb_device_destroy, + }; + + typedef struct MUSBPacket MUSBPacket; +@@ -497,10 +498,19 @@ static void musb_detach(USBPort *port) + { + MUSBState *s = (MUSBState *) port->opaque; + ++ musb_async_cancel_device(s, port->dev); ++ + musb_intr_set(s, musb_irq_disconnect, 1); + musb_session_update(s, 1, s->session); + } + ++static void musb_child_detach(USBPort *port, USBDevice *child) ++{ ++ MUSBState *s = (MUSBState *) port->opaque; ++ ++ musb_async_cancel_device(s, child); ++} ++ + static void musb_cb_tick0(void *opaque) + { + MUSBEndPoint *ep = (MUSBEndPoint *) opaque; +@@ -782,9 +792,8 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) + musb_rx_intr_set(s, epnum, 1); + } + +-static void musb_device_destroy(USBBus *bus, USBDevice *dev) ++static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) + { +- MUSBState *s = container_of(bus, MUSBState, bus); + int ep, dir; + + for (ep = 0; ep < 16; ep++) { +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index bd92c31..46f0bcb 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -124,6 +124,7 @@ struct ohci_hcca { + }; + + static void ohci_bus_stop(OHCIState *ohci); ++static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); + + /* Bitfields for the first word of an Endpoint Desciptor. */ + #define OHCI_ED_FA_SHIFT 0 +@@ -351,6 +352,8 @@ static void ohci_detach(USBPort *port1) + OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; + ++ ohci_async_cancel_device(s, port1->dev); ++ + /* set connect status */ + if (port->ctrl & OHCI_PORT_CCS) { + port->ctrl &= ~OHCI_PORT_CCS; +@@ -392,6 +395,13 @@ static void ohci_wakeup(USBPort *port1) + ohci_set_interrupt(s, intr); + } + ++static void ohci_child_detach(USBPort *port1, USBDevice *child) ++{ ++ OHCIState *s = port1->opaque; ++ ++ ohci_async_cancel_device(s, child); ++} ++ + /* Reset the controller */ + static void ohci_reset(void *opaque) + { +@@ -1673,10 +1683,8 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) + } + } + +-static void ohci_device_destroy(USBBus *bus, USBDevice *dev) ++static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) + { +- OHCIState *ohci = container_of(bus, OHCIState, bus); +- + if (ohci->async_td && ohci->usb_packet.owner == dev) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; +@@ -1700,12 +1708,12 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={ + static USBPortOps ohci_port_ops = { + .attach = ohci_attach, + .detach = ohci_detach, ++ .child_detach = ohci_child_detach, + .wakeup = ohci_wakeup, + .complete = ohci_async_complete_packet, + }; + + static USBBusOps ohci_bus_ops = { +- .device_destroy = ohci_device_destroy, + }; + + static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, +diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c +index ab635f6..a46d61a 100644 +--- a/hw/usb-uhci.c ++++ b/hw/usb-uhci.c +@@ -606,6 +606,8 @@ static void uhci_detach(USBPort *port1) + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; + ++ uhci_async_cancel_device(s, port1->dev); ++ + /* set connect status */ + if (port->ctrl & UHCI_PORT_CCS) { + port->ctrl &= ~UHCI_PORT_CCS; +@@ -620,6 +622,13 @@ static void uhci_detach(USBPort *port1) + uhci_resume(s); + } + ++static void uhci_child_detach(USBPort *port1, USBDevice *child) ++{ ++ UHCIState *s = port1->opaque; ++ ++ uhci_async_cancel_device(s, child); ++} ++ + static void uhci_wakeup(USBPort *port1) + { + UHCIState *s = port1->opaque; +@@ -1095,22 +1104,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, + register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); + } + +-static void uhci_device_destroy(USBBus *bus, USBDevice *dev) +-{ +- UHCIState *s = container_of(bus, UHCIState, bus); +- +- uhci_async_cancel_device(s, dev); +-} +- + static USBPortOps uhci_port_ops = { + .attach = uhci_attach, + .detach = uhci_detach, ++ .child_detach = uhci_child_detach, + .wakeup = uhci_wakeup, + .complete = uhci_async_complete, + }; + + static USBBusOps uhci_bus_ops = { +- .device_destroy = uhci_device_destroy, + }; + + static int usb_uhci_common_initfn(PCIDevice *dev) +diff --git a/hw/usb.h b/hw/usb.h +index 65f45a0..ded2de2 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -252,6 +252,11 @@ struct USBDeviceInfo { + typedef struct USBPortOps { + void (*attach)(USBPort *port); + void (*detach)(USBPort *port); ++ /* ++ * This gets called when a device downstream from the device attached to ++ * the port (iow attached through a hub) gets detached. ++ */ ++ void (*child_detach)(USBPort *port, USBDevice *child); + void (*wakeup)(USBPort *port); + /* + * Note that port->dev will be different then the device from which +@@ -351,7 +356,6 @@ struct USBBus { + struct USBBusOps { + int (*register_companion)(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport); +- void (*device_destroy)(USBBus *bus, USBDevice *dev); + }; + + void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); +-- +1.7.5.1 + diff --git a/0007-usb-ehci-drop-unused-num-ports-state-member.patch b/0007-usb-ehci-drop-unused-num-ports-state-member.patch new file mode 100644 index 0000000..7ea28e7 --- /dev/null +++ b/0007-usb-ehci-drop-unused-num-ports-state-member.patch @@ -0,0 +1,26 @@ +From 788de57b67ba5e14d965edb542eb58ed4603faf8 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 17 Jun 2011 15:26:29 +0200 +Subject: [PATCH 07/35] usb-ehci: drop unused num-ports state member + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 1 - + 1 files changed, 0 insertions(+), 1 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 96451f3..87e1de3 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -373,7 +373,6 @@ struct EHCIState { + qemu_irq irq; + target_phys_addr_t mem_base; + int mem; +- int num_ports; + + /* properties */ + uint32_t freq; +-- +1.7.5.1 + diff --git a/0008-usb-ehci-Connect-Status-bit-is-read-only-don-t-allow.patch b/0008-usb-ehci-Connect-Status-bit-is-read-only-don-t-allow.patch new file mode 100644 index 0000000..7ebbdaf --- /dev/null +++ b/0008-usb-ehci-Connect-Status-bit-is-read-only-don-t-allow.patch @@ -0,0 +1,32 @@ +From f7e7c102ed1c3ff7790c84f8bb9d379ad6405d6b Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 21 Jun 2011 12:12:35 +0200 +Subject: [PATCH 08/35] usb-ehci: Connect Status bit is read only, don't allow + changing it by the guest + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 87e1de3..ce1a432 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -103,10 +103,10 @@ + #define PORTSC_BEGIN PORTSC + #define PORTSC_END (PORTSC + 4 * NB_PORTS) + /* +- * Bits that are reserverd or are read-only are masked out of values ++ * Bits that are reserved or are read-only are masked out of values + * written to us by software + */ +-#define PORTSC_RO_MASK 0x007021c5 ++#define PORTSC_RO_MASK 0x007021c4 + #define PORTSC_RWC_MASK 0x0000002a + #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable + #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable +-- +1.7.5.1 + diff --git a/0009-usb-ehci-cleanup-port-reset-handling.patch b/0009-usb-ehci-cleanup-port-reset-handling.patch new file mode 100644 index 0000000..2651396 --- /dev/null +++ b/0009-usb-ehci-cleanup-port-reset-handling.patch @@ -0,0 +1,38 @@ +From afada27ad05658aae93aa8beab34b1b6885f63b9 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 21 Jun 2011 12:23:40 +0200 +Subject: [PATCH 09/35] usb-ehci: cleanup port reset handling + +Doing a usb_attach when dev is NULL will just result in the +port detach op getting called even though nothing was connected in +the first place. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 7 +------ + 1 files changed, 1 insertions(+), 6 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index ce1a432..d85e0a9 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -863,14 +863,9 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) + + if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { + trace_usb_ehci_port_reset(port, 0); +- usb_attach(&s->ports[port], dev); +- +- // TODO how to handle reset of ports with no device + if (dev) { ++ usb_attach(&s->ports[port], dev); + usb_send_msg(dev, USB_MSG_RESET); +- } +- +- if (s->ports[port].dev) { + *portsc &= ~PORTSC_CSC; + } + +-- +1.7.5.1 + diff --git a/0010-usb-assert-on-calling-usb_attach-port-NULL-on-a-port.patch b/0010-usb-assert-on-calling-usb_attach-port-NULL-on-a-port.patch new file mode 100644 index 0000000..8b86aed --- /dev/null +++ b/0010-usb-assert-on-calling-usb_attach-port-NULL-on-a-port.patch @@ -0,0 +1,44 @@ +From a7466b2ff8e1cbdf3abf08a935c3b6c19303ffc2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 14:26:18 +0200 +Subject: [PATCH 10/35] usb: assert on calling usb_attach(port, NULL) on a + port without a dev + +with the "usb-ehci: cleanup port reset handling" patch in place no callers +are calling usb_attach(port, NULL) for a port where port->dev is NULL. + +Doing that makes no sense as that causes the port detach op to get called +for a port with nothing attached. Add an assert that port->dev != NULL when +dev == NULL, and remove the check for not having a port->dev in the dev == NULL +case. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb.c | 9 ++++----- + 1 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/hw/usb.c b/hw/usb.c +index 735ffd1..27a983c 100644 +--- a/hw/usb.c ++++ b/hw/usb.c +@@ -40,12 +40,11 @@ void usb_attach(USBPort *port, USBDevice *dev) + } else { + /* detach */ + dev = port->dev; ++ assert(dev); + port->ops->detach(port); +- if (dev) { +- usb_send_msg(dev, USB_MSG_DETACH); +- dev->port = NULL; +- port->dev = NULL; +- } ++ usb_send_msg(dev, USB_MSG_DETACH); ++ dev->port = NULL; ++ port->dev = NULL; + } + } + +-- +1.7.5.1 + diff --git a/0011-usb-ehci-Fix-handling-of-PED-and-PEDC-port-status-bi.patch b/0011-usb-ehci-Fix-handling-of-PED-and-PEDC-port-status-bi.patch new file mode 100644 index 0000000..6978540 --- /dev/null +++ b/0011-usb-ehci-Fix-handling-of-PED-and-PEDC-port-status-bi.patch @@ -0,0 +1,80 @@ +From 555ef05ebba2bf68abace047e39b12de75b71181 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 14:36:13 +0200 +Subject: [PATCH 11/35] usb-ehci: Fix handling of PED and PEDC port status + bits + +The PED bit should only be set for highspeed devices and the PEDC bit +should not be set on "normal" PED bit changes, only on io errors. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 24 +++++++++++------------- + 1 files changed, 11 insertions(+), 13 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index d85e0a9..973c342 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -106,7 +106,7 @@ + * Bits that are reserved or are read-only are masked out of values + * written to us by software + */ +-#define PORTSC_RO_MASK 0x007021c4 ++#define PORTSC_RO_MASK 0x007021c0 + #define PORTSC_RWC_MASK 0x0000002a + #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable + #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable +@@ -752,7 +752,7 @@ static void ehci_detach(USBPort *port) + + ehci_queues_rip_device(s, port->dev); + +- *portsc &= ~PORTSC_CONNECT; ++ *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); + *portsc |= PORTSC_CSC; + + /* +@@ -847,16 +847,14 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) + static void handle_port_status_write(EHCIState *s, int port, uint32_t val) + { + uint32_t *portsc = &s->portsc[port]; +- int rwc; + USBDevice *dev = s->ports[port].dev; + +- rwc = val & PORTSC_RWC_MASK; ++ /* Clear rwc bits */ ++ *portsc &= ~(val & PORTSC_RWC_MASK); ++ /* The guest may clear, but not set the PED bit */ ++ *portsc &= val | ~PORTSC_PED; + val &= PORTSC_RO_MASK; + +- // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC); +- +- *portsc &= ~rwc; +- + if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { + trace_usb_ehci_port_reset(port, 1); + } +@@ -869,13 +867,13 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) + *portsc &= ~PORTSC_CSC; + } + +- /* Table 2.16 Set the enable bit(and enable bit change) to indicate ++ /* ++ * Table 2.16 Set the enable bit(and enable bit change) to indicate + * to SW that this port has a high speed device attached +- * +- * TODO - when to disable? + */ +- val |= PORTSC_PED; +- val |= PORTSC_PEDC; ++ if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) { ++ val |= PORTSC_PED; ++ } + } + + *portsc &= ~PORTSC_RO_MASK; +-- +1.7.5.1 + diff --git a/0012-usb-ehci-Add-support-for-registering-companion-contr.patch b/0012-usb-ehci-Add-support-for-registering-companion-contr.patch new file mode 100644 index 0000000..527a1bc --- /dev/null +++ b/0012-usb-ehci-Add-support-for-registering-companion-contr.patch @@ -0,0 +1,321 @@ +From 692f238a2abea35607bc8c9e76d26ae5b15518da Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 16:18:13 +0200 +Subject: [PATCH 12/35] usb-ehci: Add support for registering companion + controllers + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++---------- + 1 files changed, 144 insertions(+), 30 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 973c342..ec68c29 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -20,9 +20,6 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . +- * +- * TODO: +- * o Downstream port handoff + */ + + #include "hw.h" +@@ -106,7 +103,7 @@ + * Bits that are reserved or are read-only are masked out of values + * written to us by software + */ +-#define PORTSC_RO_MASK 0x007021c0 ++#define PORTSC_RO_MASK 0x007001c0 + #define PORTSC_RWC_MASK 0x0000002a + #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable + #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable +@@ -373,6 +370,7 @@ struct EHCIState { + qemu_irq irq; + target_phys_addr_t mem_base; + int mem; ++ int companion_count; + + /* properties */ + uint32_t freq; +@@ -408,6 +406,7 @@ struct EHCIState { + int astate; // Current state in asynchronous schedule + int pstate; // Current state in periodic schedule + USBPort ports[NB_PORTS]; ++ USBPort *companion_ports[NB_PORTS]; + uint32_t usbsts_pending; + QTAILQ_HEAD(, EHCIQueue) queues; + +@@ -730,17 +729,17 @@ static void ehci_attach(USBPort *port) + + trace_usb_ehci_port_attach(port->index, port->dev->product_desc); + ++ if (*portsc & PORTSC_POWNER) { ++ USBPort *companion = s->companion_ports[port->index]; ++ companion->dev = port->dev; ++ companion->ops->attach(companion); ++ return; ++ } ++ + *portsc |= PORTSC_CONNECT; + *portsc |= PORTSC_CSC; + +- /* +- * If a high speed device is attached then we own this port(indicated +- * by zero in the PORTSC_POWNER bit field) so set the status bit +- * and set an interrupt if enabled. +- */ +- if ( !(*portsc & PORTSC_POWNER)) { +- ehci_set_interrupt(s, USBSTS_PCD); +- } ++ ehci_set_interrupt(s, USBSTS_PCD); + } + + static void ehci_detach(USBPort *port) +@@ -750,36 +749,110 @@ static void ehci_detach(USBPort *port) + + trace_usb_ehci_port_detach(port->index); + ++ if (*portsc & PORTSC_POWNER) { ++ USBPort *companion = s->companion_ports[port->index]; ++ companion->ops->detach(companion); ++ companion->dev = NULL; ++ return; ++ } ++ + ehci_queues_rip_device(s, port->dev); + + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); + *portsc |= PORTSC_CSC; + +- /* +- * If a high speed device is attached then we own this port(indicated +- * by zero in the PORTSC_POWNER bit field) so set the status bit +- * and set an interrupt if enabled. +- */ +- if ( !(*portsc & PORTSC_POWNER)) { +- ehci_set_interrupt(s, USBSTS_PCD); +- } ++ ehci_set_interrupt(s, USBSTS_PCD); + } + + static void ehci_child_detach(USBPort *port, USBDevice *child) + { + EHCIState *s = port->opaque; ++ uint32_t portsc = s->portsc[port->index]; ++ ++ if (portsc & PORTSC_POWNER) { ++ USBPort *companion = s->companion_ports[port->index]; ++ companion->ops->child_detach(companion, child); ++ companion->dev = NULL; ++ return; ++ } + + ehci_queues_rip_device(s, child); + } + ++static void ehci_wakeup(USBPort *port) ++{ ++ EHCIState *s = port->opaque; ++ uint32_t portsc = s->portsc[port->index]; ++ ++ if (portsc & PORTSC_POWNER) { ++ USBPort *companion = s->companion_ports[port->index]; ++ if (companion->ops->wakeup) { ++ companion->ops->wakeup(companion); ++ } ++ } ++} ++ ++static int ehci_register_companion(USBBus *bus, USBPort *ports[], ++ uint32_t portcount, uint32_t firstport) ++{ ++ EHCIState *s = container_of(bus, EHCIState, bus); ++ uint32_t i; ++ ++ if (firstport + portcount > NB_PORTS) { ++ qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport", ++ "firstport on masterbus"); ++ error_printf_unless_qmp( ++ "firstport value of %u makes companion take ports %u - %u, which " ++ "is outside of the valid range of 0 - %u\n", firstport, firstport, ++ firstport + portcount - 1, NB_PORTS - 1); ++ return -1; ++ } ++ ++ for (i = 0; i < portcount; i++) { ++ if (s->companion_ports[firstport + i]) { ++ qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", ++ "an USB masterbus"); ++ error_printf_unless_qmp( ++ "port %u on masterbus %s already has a companion assigned\n", ++ firstport + i, bus->qbus.name); ++ return -1; ++ } ++ } ++ ++ for (i = 0; i < portcount; i++) { ++ s->companion_ports[firstport + i] = ports[i]; ++ s->ports[firstport + i].speedmask |= ++ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; ++ /* Ensure devs attached before the initial reset go to the companion */ ++ s->portsc[firstport + i] = PORTSC_POWNER; ++ } ++ ++ s->companion_count++; ++ s->mmio[0x05] = (s->companion_count << 4) | portcount; ++ ++ return 0; ++} ++ + /* 4.1 host controller initialization */ + static void ehci_reset(void *opaque) + { + EHCIState *s = opaque; + int i; ++ USBDevice *devs[NB_PORTS]; + + trace_usb_ehci_reset(); + ++ /* ++ * Do the detach before touching portsc, so that it correctly gets send to ++ * us or to our companion based on PORTSC_POWNER before the reset. ++ */ ++ for(i = 0; i < NB_PORTS; i++) { ++ devs[i] = s->ports[i].dev; ++ if (devs[i]) { ++ usb_attach(&s->ports[i], NULL); ++ } ++ } ++ + memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); + + s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; +@@ -791,10 +864,13 @@ static void ehci_reset(void *opaque) + s->attach_poll_counter = 0; + + for(i = 0; i < NB_PORTS; i++) { +- s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; +- +- if (s->ports[i].dev) { +- usb_attach(&s->ports[i], s->ports[i].dev); ++ if (s->companion_ports[i]) { ++ s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; ++ } else { ++ s->portsc[i] = PORTSC_PPOWER; ++ } ++ if (devs[i]) { ++ usb_attach(&s->ports[i], devs[i]); + } + } + ehci_queues_rip_all(s); +@@ -844,6 +920,34 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) + exit(1); + } + ++static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) ++{ ++ USBDevice *dev = s->ports[port].dev; ++ uint32_t *portsc = &s->portsc[port]; ++ uint32_t orig; ++ ++ if (s->companion_ports[port] == NULL) ++ return; ++ ++ owner = owner & PORTSC_POWNER; ++ orig = *portsc & PORTSC_POWNER; ++ ++ if (!(owner ^ orig)) { ++ return; ++ } ++ ++ if (dev) { ++ usb_attach(&s->ports[port], NULL); ++ } ++ ++ *portsc &= ~PORTSC_POWNER; ++ *portsc |= owner; ++ ++ if (dev) { ++ usb_attach(&s->ports[port], dev); ++ } ++} ++ + static void handle_port_status_write(EHCIState *s, int port, uint32_t val) + { + uint32_t *portsc = &s->portsc[port]; +@@ -853,6 +957,9 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) + *portsc &= ~(val & PORTSC_RWC_MASK); + /* The guest may clear, but not set the PED bit */ + *portsc &= val | ~PORTSC_PED; ++ /* POWNER is masked out by RO_MASK as it is RO when we've no companion */ ++ handle_port_owner_write(s, port, val); ++ /* And finally apply RO_MASK */ + val &= PORTSC_RO_MASK; + + if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { +@@ -956,7 +1063,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) + val &= 0x1; + if (val) { + for(i = 0; i < NB_PORTS; i++) +- s->portsc[i] &= ~PORTSC_POWNER; ++ handle_port_owner_write(s, i, 0); + } + break; + +@@ -1114,8 +1221,17 @@ static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) + + static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) + { +- EHCIQueue *q = container_of(packet, EHCIQueue, packet); ++ EHCIQueue *q; ++ EHCIState *s = port->opaque; ++ uint32_t portsc = s->portsc[port->index]; ++ ++ if (portsc & PORTSC_POWNER) { ++ USBPort *companion = s->companion_ports[port->index]; ++ companion->ops->complete(companion, packet); ++ return; ++ } + ++ q = container_of(packet, EHCIQueue, packet); + trace_usb_ehci_queue_action(q, "wakeup"); + assert(q->async == EHCI_ASYNC_INFLIGHT); + q->async = EHCI_ASYNC_FINISHED; +@@ -1245,8 +1361,6 @@ static int ehci_execute(EHCIQueue *q) + port = &q->ehci->ports[i]; + dev = port->dev; + +- // TODO sometime we will also need to check if we are the port owner +- + if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { + DPRINTF("Port %d, no exec, not connected(%08X)\n", + i, q->ehci->portsc[i]); +@@ -1339,8 +1453,6 @@ static int ehci_process_itd(EHCIState *ehci, + port = &ehci->ports[j]; + dev = port->dev; + +- // TODO sometime we will also need to check if we are the port owner +- + if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { + continue; + } +@@ -2124,10 +2236,12 @@ static USBPortOps ehci_port_ops = { + .attach = ehci_attach, + .detach = ehci_detach, + .child_detach = ehci_child_detach, ++ .wakeup = ehci_wakeup, + .complete = ehci_async_complete_packet, + }; + + static USBBusOps ehci_bus_ops = { ++ .register_companion = ehci_register_companion, + }; + + static PCIDeviceInfo ehci_info = { +-- +1.7.5.1 + diff --git a/0013-usb-uhci-Add-support-for-being-a-companion-controlle.patch b/0013-usb-uhci-Add-support-for-being-a-companion-controlle.patch new file mode 100644 index 0000000..bb28f62 --- /dev/null +++ b/0013-usb-uhci-Add-support-for-being-a-companion-controlle.patch @@ -0,0 +1,103 @@ +From 850d218026df41324430af62063f68afe652a7ac Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 17:44:53 +0200 +Subject: [PATCH 13/35] usb-uhci: Add support for being a companion controller + +To use as a companion controller set the masterbus property. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-uhci.c | 41 ++++++++++++++++++++++++++++++++++++----- + 1 files changed, 36 insertions(+), 5 deletions(-) + +diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c +index a46d61a..925c03b 100644 +--- a/hw/usb-uhci.c ++++ b/hw/usb-uhci.c +@@ -132,7 +132,7 @@ typedef struct UHCIPort { + + struct UHCIState { + PCIDevice dev; +- USBBus bus; ++ USBBus bus; /* Note unused when we're a companion controller */ + uint16_t cmd; /* cmd register */ + uint16_t status; + uint16_t intr; /* interrupt enable register */ +@@ -150,6 +150,10 @@ struct UHCIState { + /* Active packets */ + QTAILQ_HEAD(,UHCIAsync) async_pending; + uint8_t num_ports_vmstate; ++ ++ /* Properties */ ++ char *masterbus; ++ uint32_t firstport; + }; + + typedef struct UHCI_TD { +@@ -1126,10 +1130,22 @@ static int usb_uhci_common_initfn(PCIDevice *dev) + pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3 + pci_conf[USB_SBRN] = USB_RELEASE_1; // release number + +- usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); +- 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); ++ if (s->masterbus) { ++ USBPort *ports[NB_PORTS]; ++ for(i = 0; i < NB_PORTS; i++) { ++ ports[i] = &s->ports[i].port; ++ } ++ if (usb_register_companion(s->masterbus, ports, NB_PORTS, ++ s->firstport, s, &uhci_port_ops, ++ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { ++ return -1; ++ } ++ } else { ++ usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); ++ 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); ++ } + } + s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); + s->num_ports_vmstate = NB_PORTS; +@@ -1170,6 +1186,11 @@ static PCIDeviceInfo uhci_info[] = { + .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = (Property[]) { ++ DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), ++ DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), ++ DEFINE_PROP_END_OF_LIST(), ++ }, + },{ + .qdev.name = "piix4-usb-uhci", + .qdev.size = sizeof(UHCIState), +@@ -1179,6 +1200,11 @@ static PCIDeviceInfo uhci_info[] = { + .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = (Property[]) { ++ DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), ++ DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), ++ DEFINE_PROP_END_OF_LIST(), ++ }, + },{ + .qdev.name = "vt82c686b-usb-uhci", + .qdev.size = sizeof(UHCIState), +@@ -1188,6 +1214,11 @@ static PCIDeviceInfo uhci_info[] = { + .device_id = PCI_DEVICE_ID_VIA_UHCI, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = (Property[]) { ++ DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), ++ DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), ++ DEFINE_PROP_END_OF_LIST(), ++ }, + },{ + /* end of list */ + } +-- +1.7.5.1 + diff --git a/0014-usb-ohci-Add-support-for-being-a-companion-controlle.patch b/0014-usb-ohci-Add-support-for-being-a-companion-controlle.patch new file mode 100644 index 0000000..e0688bf --- /dev/null +++ b/0014-usb-ohci-Add-support-for-being-a-companion-controlle.patch @@ -0,0 +1,127 @@ +From 90ac519e89c71ac9f9731b21cef510b5cbaee38b Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Jun 2011 20:29:05 +0200 +Subject: [PATCH 14/35] usb-ohci: Add support for being a companion controller + +To use as a companion controller, use pci-ohci as device and set the +masterbus and num-ports properties, ie: + +-device usb-ehci,addr=0b.1,multifunction=on,id=ehci0 +-device pci-ohci,addr=0b.0,multifunction=on,masterbus=ehci0.0,num-ports=4 + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ohci.c | 52 ++++++++++++++++++++++++++++++++++++++++------------ + 1 files changed, 40 insertions(+), 12 deletions(-) + +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index 46f0bcb..c77a20e 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -1716,8 +1716,9 @@ static USBPortOps ohci_port_ops = { + static USBBusOps ohci_bus_ops = { + }; + +-static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, +- int num_ports, uint32_t localmem_base) ++static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, ++ int num_ports, uint32_t localmem_base, ++ char *masterbus, uint32_t firstport) + { + int i; + +@@ -1737,38 +1738,58 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, + usb_frame_time, usb_bit_time); + } + ++ ohci->num_ports = num_ports; ++ if (masterbus) { ++ USBPort *ports[OHCI_MAX_PORTS]; ++ for(i = 0; i < num_ports; i++) { ++ ports[i] = &ohci->rhport[i].port; ++ } ++ if (usb_register_companion(masterbus, ports, num_ports, ++ firstport, ohci, &ohci_port_ops, ++ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { ++ return -1; ++ } ++ } else { ++ usb_bus_new(&ohci->bus, &ohci_bus_ops, 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); ++ } ++ } ++ + ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci, + DEVICE_LITTLE_ENDIAN); + ohci->localmem_base = localmem_base; + + ohci->name = dev->info->name; + +- usb_bus_new(&ohci->bus, &ohci_bus_ops, dev); +- ohci->num_ports = num_ports; +- 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); +- } +- + ohci->async_td = 0; + qemu_register_reset(ohci_reset, ohci); ++ ++ return 0; + } + + typedef struct { + PCIDevice pci_dev; + OHCIState state; ++ char *masterbus; ++ uint32_t num_ports; ++ uint32_t firstport; + } OHCIPCIState; + + static int usb_ohci_initfn_pci(struct PCIDevice *dev) + { + OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev); +- int num_ports = 3; + + ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */ + /* TODO: RST# value should be 0. */ + ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */ + +- usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0); ++ if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0, ++ ohci->masterbus, ohci->firstport) != 0) { ++ return -1; ++ } + ohci->state.irq = ohci->pci_dev.irq[0]; + + /* TODO: avoid cast below by using dev */ +@@ -1792,7 +1813,8 @@ static int ohci_init_pxa(SysBusDevice *dev) + { + OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev); + +- usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset); ++ /* Cannot fail as we pass NULL for masterbus */ ++ usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0); + sysbus_init_irq(dev, &s->ohci.irq); + sysbus_init_mmio(dev, 0x1000, s->ohci.mem); + +@@ -1807,6 +1829,12 @@ static PCIDeviceInfo ohci_pci_info = { + .vendor_id = PCI_VENDOR_ID_APPLE, + .device_id = PCI_DEVICE_ID_APPLE_IPID_USB, + .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = (Property[]) { ++ DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), ++ DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), ++ DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), ++ DEFINE_PROP_END_OF_LIST(), ++ }, + }; + + static SysBusDeviceInfo ohci_sysbus_info = { +-- +1.7.5.1 + diff --git a/0015-pci-add-ich9-usb-controller-ids.patch b/0015-pci-add-ich9-usb-controller-ids.patch new file mode 100644 index 0000000..64c6f37 --- /dev/null +++ b/0015-pci-add-ich9-usb-controller-ids.patch @@ -0,0 +1,31 @@ +From 79a91f584ad187b6f159209b3aff3d3d310e78c2 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 1 Jul 2011 11:45:02 +0200 +Subject: [PATCH 15/35] pci: add ich9 usb controller ids + +Signed-off-by: Gerd Hoffmann +--- + hw/pci_ids.h | 8 ++++++++ + 1 files changed, 8 insertions(+), 0 deletions(-) + +diff --git a/hw/pci_ids.h b/hw/pci_ids.h +index d94578c..927f2b0 100644 +--- a/hw/pci_ids.h ++++ b/hw/pci_ids.h +@@ -109,5 +109,13 @@ + #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 + #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 + #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 ++#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 ++#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 ++#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 ++#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 ++#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 ++#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 ++#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a ++#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c + + #define PCI_VENDOR_ID_XENSOURCE 0x5853 +-- +1.7.5.1 + diff --git a/0016-uhci-add-ich9-controllers.patch b/0016-uhci-add-ich9-controllers.patch new file mode 100644 index 0000000..c5b6897 --- /dev/null +++ b/0016-uhci-add-ich9-controllers.patch @@ -0,0 +1,102 @@ +From 18f499ba7cac5d66f42255f6ddf384e01bead569 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 1 Jul 2011 09:48:49 +0200 +Subject: [PATCH 16/35] uhci: add ich9 controllers + +Add ich9 controllers, Factor out properties to a separate +struct and reference it to reduce duplication. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-uhci.c | 54 +++++++++++++++++++++++++++++++++++++++--------------- + 1 files changed, 39 insertions(+), 15 deletions(-) + +diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c +index 925c03b..2ef4c5b 100644 +--- a/hw/usb-uhci.c ++++ b/hw/usb-uhci.c +@@ -1176,6 +1176,12 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) + return usb_uhci_common_initfn(dev); + } + ++static Property uhci_properties[] = { ++ DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), ++ DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), ++ DEFINE_PROP_END_OF_LIST(), ++}; ++ + static PCIDeviceInfo uhci_info[] = { + { + .qdev.name = "piix3-usb-uhci", +@@ -1186,11 +1192,7 @@ static PCIDeviceInfo uhci_info[] = { + .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, +- .qdev.props = (Property[]) { +- DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), +- DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), +- DEFINE_PROP_END_OF_LIST(), +- }, ++ .qdev.props = uhci_properties, + },{ + .qdev.name = "piix4-usb-uhci", + .qdev.size = sizeof(UHCIState), +@@ -1200,11 +1202,7 @@ static PCIDeviceInfo uhci_info[] = { + .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, +- .qdev.props = (Property[]) { +- DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), +- DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), +- DEFINE_PROP_END_OF_LIST(), +- }, ++ .qdev.props = uhci_properties, + },{ + .qdev.name = "vt82c686b-usb-uhci", + .qdev.size = sizeof(UHCIState), +@@ -1214,11 +1212,37 @@ static PCIDeviceInfo uhci_info[] = { + .device_id = PCI_DEVICE_ID_VIA_UHCI, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, +- .qdev.props = (Property[]) { +- DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), +- DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), +- DEFINE_PROP_END_OF_LIST(), +- }, ++ .qdev.props = uhci_properties, ++ },{ ++ .qdev.name = "ich9-usb-uhci1", ++ .qdev.size = sizeof(UHCIState), ++ .qdev.vmsd = &vmstate_uhci, ++ .init = usb_uhci_common_initfn, ++ .vendor_id = PCI_VENDOR_ID_INTEL, ++ .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, ++ .revision = 0x03, ++ .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = uhci_properties, ++ },{ ++ .qdev.name = "ich9-usb-uhci2", ++ .qdev.size = sizeof(UHCIState), ++ .qdev.vmsd = &vmstate_uhci, ++ .init = usb_uhci_common_initfn, ++ .vendor_id = PCI_VENDOR_ID_INTEL, ++ .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, ++ .revision = 0x03, ++ .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = uhci_properties, ++ },{ ++ .qdev.name = "ich9-usb-uhci3", ++ .qdev.size = sizeof(UHCIState), ++ .qdev.vmsd = &vmstate_uhci, ++ .init = usb_uhci_common_initfn, ++ .vendor_id = PCI_VENDOR_ID_INTEL, ++ .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, ++ .revision = 0x03, ++ .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = uhci_properties, + },{ + /* end of list */ + } +-- +1.7.5.1 + diff --git a/0017-ehci-fix-port-count.patch b/0017-ehci-fix-port-count.patch new file mode 100644 index 0000000..13253f7 --- /dev/null +++ b/0017-ehci-fix-port-count.patch @@ -0,0 +1,28 @@ +From 62ffcd73894343e42b28eb1c4746ef706bd237b3 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 1 Jul 2011 09:56:43 +0200 +Subject: [PATCH 17/35] ehci: fix port count. + +The ICH4 EHCI controller which we emulate has six ports not four. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index ec68c29..0b959ca 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -130,7 +130,7 @@ + #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) + + #define NB_MAXINTRATE 8 // Max rate at which controller issues ints +-#define NB_PORTS 4 // Number of downstream ports ++#define NB_PORTS 6 // Number of downstream ports + #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction + #define MAX_ITERATIONS 20 // Max number of QH before we break the loop + #define MAX_QH 100 // Max allowable queue heads in a chain +-- +1.7.5.1 + diff --git a/0018-ehci-add-ich9-controller.patch b/0018-ehci-add-ich9-controller.patch new file mode 100644 index 0000000..dcf3a56 --- /dev/null +++ b/0018-ehci-add-ich9-controller.patch @@ -0,0 +1,74 @@ +From 8fe1c377cf894b7071e36e64a206f3b52ea0ab68 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 1 Jul 2011 11:51:02 +0200 +Subject: [PATCH 18/35] ehci: add ich9 controller. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 43 +++++++++++++++++++++++++++++-------------- + 1 files changed, 29 insertions(+), 14 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 0b959ca..a4758f9 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -2244,19 +2244,34 @@ static USBBusOps ehci_bus_ops = { + .register_companion = ehci_register_companion, + }; + +-static PCIDeviceInfo ehci_info = { +- .qdev.name = "usb-ehci", +- .qdev.size = sizeof(EHCIState), +- .init = usb_ehci_initfn, +- .vendor_id = PCI_VENDOR_ID_INTEL, +- .device_id = PCI_DEVICE_ID_INTEL_82801D, +- .revision = 0x10, +- .class_id = PCI_CLASS_SERIAL_USB, +- .qdev.props = (Property[]) { +- DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), +- DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), +- DEFINE_PROP_END_OF_LIST(), +- }, ++static Property ehci_properties[] = { ++ DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), ++ DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), ++ DEFINE_PROP_END_OF_LIST(), ++}; ++ ++static PCIDeviceInfo ehci_info[] = { ++ { ++ .qdev.name = "usb-ehci", ++ .qdev.size = sizeof(EHCIState), ++ .init = usb_ehci_initfn, ++ .vendor_id = PCI_VENDOR_ID_INTEL, ++ .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ ++ .revision = 0x10, ++ .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = ehci_properties, ++ },{ ++ .qdev.name = "ich9-usb-ehci1", ++ .qdev.size = sizeof(EHCIState), ++ .init = usb_ehci_initfn, ++ .vendor_id = PCI_VENDOR_ID_INTEL, ++ .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, ++ .revision = 0x03, ++ .class_id = PCI_CLASS_SERIAL_USB, ++ .qdev.props = ehci_properties, ++ },{ ++ /* end of list */ ++ } + }; + + static int usb_ehci_initfn(PCIDevice *dev) +@@ -2335,7 +2350,7 @@ static int usb_ehci_initfn(PCIDevice *dev) + + static void ehci_register(void) + { +- pci_qdev_register(&ehci_info); ++ pci_qdev_register_many(ehci_info); + } + device_init(ehci_register); + +-- +1.7.5.1 + diff --git a/0019-usb-update-documentation.patch b/0019-usb-update-documentation.patch new file mode 100644 index 0000000..af65a25 --- /dev/null +++ b/0019-usb-update-documentation.patch @@ -0,0 +1,112 @@ +From 097e10ae52ea3c4fd2ac9b019a1de9a4649e8f2e Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 5 Jul 2011 16:58:41 +0200 +Subject: [PATCH 19/35] usb: update documentation + +Add a paragraph on companion controller mode and a +configuration file which sets it all up for you. + +Signed-off-by: Gerd Hoffmann +--- + docs/ich9-ehci-uhci.cfg | 37 +++++++++++++++++++++++++++++++++++++ + docs/usb2.txt | 33 ++++++++++++++++++++++++++++----- + 2 files changed, 65 insertions(+), 5 deletions(-) + create mode 100644 docs/ich9-ehci-uhci.cfg + +diff --git a/docs/ich9-ehci-uhci.cfg b/docs/ich9-ehci-uhci.cfg +new file mode 100644 +index 0000000..a0e9b96 +--- /dev/null ++++ b/docs/ich9-ehci-uhci.cfg +@@ -0,0 +1,37 @@ ++########################################################################### ++# ++# You can pass this file directly to qemu using the -readconfig ++# command line switch. ++# ++# This config file creates a EHCI adapter with companion UHCI ++# controllers as multifunction device in PCI slot "1d". ++# ++# Specify "bus=ehci.0" when creating usb devices to hook them up ++# there. ++# ++ ++[device "ehci"] ++ driver = "ich9-usb-ehci1" ++ addr = "1d.7" ++ multifunction = "on" ++ ++[device "uhci-1"] ++ driver = "ich9-usb-uhci1" ++ addr = "1d.0" ++ multifunction = "on" ++ masterbus = "ehci.0" ++ firstport = "0" ++ ++[device "uhci-2"] ++ driver = "ich9-usb-uhci2" ++ addr = "1d.1" ++ multifunction = "on" ++ masterbus = "ehci.0" ++ firstport = "2" ++ ++[device "uhci-3"] ++ driver = "ich9-usb-uhci3" ++ addr = "1d.2" ++ multifunction = "on" ++ masterbus = "ehci.0" ++ firstport = "4" +diff --git a/docs/usb2.txt b/docs/usb2.txt +index 5950c71..228aa33 100644 +--- a/docs/usb2.txt ++++ b/docs/usb2.txt +@@ -2,11 +2,13 @@ + USB 2.0 Quick Start + =================== + +-The QEMU EHCI Adapter does *not* support companion controllers. That +-implies there are two completely separate USB busses: One USB 1.1 bus +-driven by the UHCI controller and one USB 2.0 bus driven by the EHCI +-controller. Devices must be attached to the correct controller +-manually. ++The QEMU EHCI Adapter can be used with and without companion ++controllers. See below for the companion controller mode. ++ ++When not running in companion controller mode there are two completely ++separate USB busses: One USB 1.1 bus driven by the UHCI controller and ++one USB 2.0 bus driven by the EHCI controller. Devices must be ++attached to the correct controller manually. + + The '-usb' switch will make qemu create the UHCI controller as part of + the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0". +@@ -32,6 +34,27 @@ This attaches a usb tablet to the UHCI adapter and a usb mass storage + device to the EHCI adapter. + + ++Companion controller support ++---------------------------- ++ ++Companion controller support has been added recently. The operational ++model described above with two completely separate busses still works ++fine. Additionally the UHCI and OHCI controllers got the ability to ++attach to a usb bus created by EHCI as companion controllers. This is ++done by specifying the masterbus and firstport properties. masterbus ++specifies the bus name the controller should attach to. firstport ++specifies the first port the controller should attach to, which is ++needed as usually one ehci controller with six ports has three uhci ++companion controllers with two ports each. ++ ++There is a config file in docs which will do all this for you, just ++try ... ++ ++ qemu -readconfig docs/ich9-ehci-uhci.cfg ++ ++... then use "bus=ehci.0" to assign your usb devices to that bus. ++ ++ + More USB tips & tricks + ====================== + +-- +1.7.5.1 + diff --git a/0020-usb_register_port-do-not-set-port-opaque-and-port-in.patch b/0020-usb_register_port-do-not-set-port-opaque-and-port-in.patch new file mode 100644 index 0000000..ff3d32e --- /dev/null +++ b/0020-usb_register_port-do-not-set-port-opaque-and-port-in.patch @@ -0,0 +1,28 @@ +From 88a8f55b17e9e6c78ce620ac91012bfd822f8f04 Mon Sep 17 00:00:00 2001 +From: Jes Sorensen +Date: Mon, 4 Jul 2011 17:33:05 +0200 +Subject: [PATCH 20/35] usb_register_port(): do not set port->opaque and + port->index twice + +Signed-off-by: Jes Sorensen +Signed-off-by: Gerd Hoffmann +--- + hw/usb-bus.c | 2 -- + 1 files changed, 0 insertions(+), 2 deletions(-) + +diff --git a/hw/usb-bus.c b/hw/usb-bus.c +index c8347e9..f1dd55e 100644 +--- a/hw/usb-bus.c ++++ b/hw/usb-bus.c +@@ -143,8 +143,6 @@ static void usb_fill_port(USBPort *port, void *opaque, int index, + { + port->opaque = opaque; + port->index = index; +- port->opaque = opaque; +- port->index = index; + port->ops = ops; + port->speedmask = speedmask; + usb_port_location(port, NULL, index + 1); +-- +1.7.5.1 + diff --git a/0021-usb-fixup-bluetooth-descriptors.patch b/0021-usb-fixup-bluetooth-descriptors.patch new file mode 100644 index 0000000..d81d1b2 --- /dev/null +++ b/0021-usb-fixup-bluetooth-descriptors.patch @@ -0,0 +1,116 @@ +From 1d729b8511fd72f66d7cc10e801a546daa40bb79 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 6 Jul 2011 12:40:28 +0200 +Subject: [PATCH 21/35] usb: fixup bluetooth descriptors + +Commit 4696425cd05c7baa0a4b469d43ba4b8488bcfc0f changes some +endpoints from isocrounous to interrupt by mistake. Fix it. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-bt.c | 24 ++++++++++++------------ + 1 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/hw/usb-bt.c b/hw/usb-bt.c +index baae487..e364513 100644 +--- a/hw/usb-bt.c ++++ b/hw/usb-bt.c +@@ -99,13 +99,13 @@ static const USBDescIface desc_iface_bluetooth[] = { + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0, + .bInterval = 0x01, + }, +@@ -120,13 +120,13 @@ static const USBDescIface desc_iface_bluetooth[] = { + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x09, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x09, + .bInterval = 0x01, + }, +@@ -141,13 +141,13 @@ static const USBDescIface desc_iface_bluetooth[] = { + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x11, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x11, + .bInterval = 0x01, + }, +@@ -162,13 +162,13 @@ static const USBDescIface desc_iface_bluetooth[] = { + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x19, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x19, + .bInterval = 0x01, + }, +@@ -183,13 +183,13 @@ static const USBDescIface desc_iface_bluetooth[] = { + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x21, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x21, + .bInterval = 0x01, + }, +@@ -204,13 +204,13 @@ static const USBDescIface desc_iface_bluetooth[] = { + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x31, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, +- .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0x31, + .bInterval = 0x01, + }, +-- +1.7.5.1 + diff --git a/0022-usb-hub-remove-unused-descriptor-arrays.patch b/0022-usb-hub-remove-unused-descriptor-arrays.patch new file mode 100644 index 0000000..3f5aa9a --- /dev/null +++ b/0022-usb-hub-remove-unused-descriptor-arrays.patch @@ -0,0 +1,95 @@ +From d9c7f506bea5ed587ecf2178276e4bf82e370a67 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 7 Jul 2011 15:02:58 +0200 +Subject: [PATCH 22/35] usb-hub: remove unused descriptor arrays + +Somehow they where left over when converting the hub +to the new usb descriptor infrastructure ... + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-hub.c | 68 ---------------------------------------------------------- + 1 files changed, 0 insertions(+), 68 deletions(-) + +diff --git a/hw/usb-hub.c b/hw/usb-hub.c +index b7557ce..b49a2fe 100644 +--- a/hw/usb-hub.c ++++ b/hw/usb-hub.c +@@ -138,74 +138,6 @@ static const USBDesc desc_hub = { + .str = desc_strings, + }; + +-static const uint8_t qemu_hub_dev_descriptor[] = { +- 0x12, /* u8 bLength; */ +- 0x01, /* u8 bDescriptorType; Device */ +- 0x10, 0x01, /* u16 bcdUSB; v1.1 */ +- +- 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ +- 0x00, /* u8 bDeviceSubClass; */ +- 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ +- 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ +- +- 0x00, 0x00, /* u16 idVendor; */ +- 0x00, 0x00, /* u16 idProduct; */ +- 0x01, 0x01, /* u16 bcdDevice */ +- +- 0x03, /* u8 iManufacturer; */ +- 0x02, /* u8 iProduct; */ +- 0x01, /* u8 iSerialNumber; */ +- 0x01 /* u8 bNumConfigurations; */ +-}; +- +-/* XXX: patch interrupt size */ +-static const uint8_t qemu_hub_config_descriptor[] = { +- +- /* one configuration */ +- 0x09, /* u8 bLength; */ +- 0x02, /* u8 bDescriptorType; Configuration */ +- 0x19, 0x00, /* u16 wTotalLength; */ +- 0x01, /* u8 bNumInterfaces; (1) */ +- 0x01, /* u8 bConfigurationValue; */ +- 0x00, /* u8 iConfiguration; */ +- 0xe0, /* u8 bmAttributes; +- Bit 7: must be set, +- 6: Self-powered, +- 5: Remote wakeup, +- 4..0: resvd */ +- 0x00, /* u8 MaxPower; */ +- +- /* USB 1.1: +- * USB 2.0, single TT organization (mandatory): +- * one interface, protocol 0 +- * +- * USB 2.0, multiple TT organization (optional): +- * two interfaces, protocols 1 (like single TT) +- * and 2 (multiple TT mode) ... config is +- * sometimes settable +- * NOT IMPLEMENTED +- */ +- +- /* one interface */ +- 0x09, /* u8 if_bLength; */ +- 0x04, /* u8 if_bDescriptorType; Interface */ +- 0x00, /* u8 if_bInterfaceNumber; */ +- 0x00, /* u8 if_bAlternateSetting; */ +- 0x01, /* u8 if_bNumEndpoints; */ +- 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ +- 0x00, /* u8 if_bInterfaceSubClass; */ +- 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ +- 0x00, /* u8 if_iInterface; */ +- +- /* one endpoint (status change endpoint) */ +- 0x07, /* u8 ep_bLength; */ +- 0x05, /* u8 ep_bDescriptorType; Endpoint */ +- 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ +- 0x03, /* u8 ep_bmAttributes; Interrupt */ +- 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ +- 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +-}; +- + static const uint8_t qemu_hub_hub_descriptor[] = + { + 0x00, /* u8 bLength; patched in later */ +-- +1.7.5.1 + diff --git a/0023-usb-ohci-raise-interrupt-on-attach.patch b/0023-usb-ohci-raise-interrupt-on-attach.patch new file mode 100644 index 0000000..63dd15d --- /dev/null +++ b/0023-usb-ohci-raise-interrupt-on-attach.patch @@ -0,0 +1,50 @@ +From a0559e6445bf2cceba279bf3bcdc062497872db1 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 7 Jul 2011 15:18:50 +0200 +Subject: [PATCH 23/35] usb-ohci: raise interrupt on attach + +Got lost in commit 618c169b577db64ac6589ad48825d2e11760d1a6, +add it back in. Also fix codestyle while we are at it. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ohci.c | 8 +++++++- + 1 files changed, 7 insertions(+), 1 deletions(-) + +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index c77a20e..8491d59 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -327,6 +327,7 @@ static void ohci_attach(USBPort *port1) + { + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; ++ uint32_t old_state = port->ctrl; + + /* set connect status */ + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; +@@ -344,6 +345,10 @@ static void ohci_attach(USBPort *port1) + } + + DPRINTF("usb-ohci: Attached port %d\n", port1->index); ++ ++ if (old_state != port->ctrl) { ++ ohci_set_interrupt(s, OHCI_INTR_RHSC); ++ } + } + + static void ohci_detach(USBPort *port1) +@@ -366,8 +371,9 @@ static void ohci_detach(USBPort *port1) + } + DPRINTF("usb-ohci: Detached port %d\n", port1->index); + +- if (old_state != port->ctrl) ++ if (old_state != port->ctrl) { + ohci_set_interrupt(s, OHCI_INTR_RHSC); ++ } + } + + static void ohci_wakeup(USBPort *port1) +-- +1.7.5.1 + diff --git a/0024-USB-add-usb-network-redirection-support.patch b/0024-USB-add-usb-network-redirection-support.patch new file mode 100644 index 0000000..88ebda4 --- /dev/null +++ b/0024-USB-add-usb-network-redirection-support.patch @@ -0,0 +1,1346 @@ +From 961391dad70649ac54a3011b5f9ba54f44d476e6 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 31 May 2011 14:59:41 +0200 +Subject: [PATCH 24/35] USB: add usb network redirection support + +This patch adds support for a usb-redir device, which takes a chardev +as a communication channel to an actual usbdevice using the usbredir protocol. + +Compiling the usb-redir device requires usbredir-0.3 to be installed for +the usbredir protocol parser, usbredir-0.3 also contains a server for +redirecting usb traffic from an actual usb device. You can get the 0.3 +release of usbredir here: +http://people.fedoraproject.org/~jwrdegoede/usbredir-0.3.tar.bz2 +(getting a more formal site for it is a WIP) + +Example usage: +1) Start usbredirserver for a usb device: +sudo usbredirserver 045e:0772 +2) Start qemu with usb2 support + a chardev talking to usbredirserver + + a usb-redir device using this chardev: +qemu ... \ + -readconfig docs/ich9-ehci-uhci.cfg \ + -chardev socket,id=usbredirchardev,host=localhost,port=4000 \ + -device usb-redir,chardev=usbredirchardev,id=usbredirdev + +Signed-off-by: Hans de Goede +--- + Makefile.objs | 1 + + configure | 28 ++ + usb-redir.c | 1218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1247 insertions(+), 0 deletions(-) + create mode 100644 usb-redir.c + +diff --git a/Makefile.objs b/Makefile.objs +index dbde6b1..ed8cfa2 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -208,6 +208,7 @@ hw-obj-$(CONFIG_HPET) += hpet.o + hw-obj-$(CONFIG_APPLESMC) += applesmc.o + hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o + hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o ++hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o + + # PPC devices + hw-obj-$(CONFIG_OPENPIC) += openpic.o +diff --git a/configure b/configure +index a896f11..4576f21 100755 +--- a/configure ++++ b/configure +@@ -190,6 +190,7 @@ spice="" + rbd="" + smartcard="" + smartcard_nss="" ++usb_redir="" + opengl="" + + # parse CC options first +@@ -773,6 +774,10 @@ for opt do + ;; + --enable-smartcard-nss) smartcard_nss="yes" + ;; ++ --disable-usb-redir) usb_redir="no" ++ ;; ++ --enable-usb-redir) usb_redir="yes" ++ ;; + *) echo "ERROR: unknown option $opt"; show_help="yes" + ;; + esac +@@ -1053,6 +1058,8 @@ echo " --disable-smartcard disable smartcard support" + echo " --enable-smartcard enable smartcard support" + echo " --disable-smartcard-nss disable smartcard nss support" + echo " --enable-smartcard-nss enable smartcard nss support" ++echo " --disable-usb-redir disable usb network redirection support" ++echo " --enable-usb-redir enable usb network redirection support" + echo "" + echo "NOTE: The object files are built at the place where configure is launched" + exit 1 +@@ -2427,6 +2434,22 @@ if test "$smartcard" = "no" ; then + smartcard_nss="no" + fi + ++# check for usbredirparser for usb network redirection support ++if test "$usb_redir" != "no" ; then ++ if $pkg_config libusbredirparser >/dev/null 2>&1 ; then ++ usb_redir="yes" ++ usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) ++ usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) ++ QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" ++ LIBS="$LIBS $usb_redir_libs" ++ else ++ if test "$usb_redir" = "yes"; then ++ feature_not_found "usb-redir" ++ fi ++ usb_redir="no" ++ fi ++fi ++ + ########################################## + + ########################################## +@@ -2676,6 +2699,7 @@ echo "spice support $spice" + echo "rbd support $rbd" + echo "xfsctl support $xfs" + echo "nss used $smartcard_nss" ++echo "usb net redir $usb_redir" + echo "OpenGL support $opengl" + + if test $sdl_too_old = "yes"; then +@@ -2974,6 +2998,10 @@ if test "$smartcard_nss" = "yes" ; then + echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak + fi + ++if test "$usb_redir" = "yes" ; then ++ echo "CONFIG_USB_REDIR=y" >> $config_host_mak ++fi ++ + if test "$opengl" = "yes" ; then + echo "CONFIG_OPENGL=y" >> $config_host_mak + fi +diff --git a/usb-redir.c b/usb-redir.c +new file mode 100644 +index 0000000..e212993 +--- /dev/null ++++ b/usb-redir.c +@@ -0,0 +1,1218 @@ ++/* ++ * USB redirector usb-guest ++ * ++ * Copyright (c) 2011 Red Hat, Inc. ++ * ++ * Red Hat Authors: ++ * Hans de Goede ++ * ++ * 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-timer.h" ++#include "monitor.h" ++#include "sysemu.h" ++ ++#include ++#include ++#include ++#include ++ ++#include "hw/usb.h" ++ ++#define MAX_ENDPOINTS 32 ++#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) ++#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) ++ ++typedef struct AsyncURB AsyncURB; ++typedef struct USBRedirDevice USBRedirDevice; ++ ++/* Struct to hold buffered packets (iso or int input packets) */ ++struct buf_packet { ++ uint8_t *data; ++ int len; ++ int status; ++ QTAILQ_ENTRY(buf_packet)next; ++}; ++ ++struct endp_data { ++ uint8_t type; ++ uint8_t interval; ++ uint8_t interface; /* bInterfaceNumber this ep belongs to */ ++ uint8_t iso_started; ++ uint8_t iso_error; /* For reporting iso errors to the HC */ ++ uint8_t interrupt_started; ++ uint8_t interrupt_error; ++ QTAILQ_HEAD(, buf_packet) bufpq; ++}; ++ ++struct USBRedirDevice { ++ USBDevice dev; ++ /* Properties */ ++ CharDriverState *cs; ++ uint8_t debug; ++ /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ ++ const uint8_t *read_buf; ++ int read_buf_size; ++ /* For async handling of open/close */ ++ QEMUBH *open_close_bh; ++ /* To delay the usb attach in case of quick chardev close + open */ ++ QEMUTimer *attach_timer; ++ int64_t next_attach_time; ++ struct usbredirparser *parser; ++ struct endp_data endpoint[MAX_ENDPOINTS]; ++ uint32_t packet_id; ++ QTAILQ_HEAD(, AsyncURB) asyncq; ++}; ++ ++struct AsyncURB { ++ USBRedirDevice *dev; ++ USBPacket *packet; ++ uint32_t packet_id; ++ int get; ++ union { ++ struct usb_redir_control_packet_header control_packet; ++ struct usb_redir_bulk_packet_header bulk_packet; ++ struct usb_redir_interrupt_packet_header interrupt_packet; ++ }; ++ QTAILQ_ENTRY(AsyncURB)next; ++}; ++ ++static void usbredir_device_connect(void *priv, ++ struct usb_redir_device_connect_header *device_connect); ++static void usbredir_device_disconnect(void *priv); ++static void usbredir_interface_info(void *priv, ++ struct usb_redir_interface_info_header *interface_info); ++static void usbredir_ep_info(void *priv, ++ struct usb_redir_ep_info_header *ep_info); ++static void usbredir_configuration_status(void *priv, uint32_t id, ++ struct usb_redir_configuration_status_header *configuration_status); ++static void usbredir_alt_setting_status(void *priv, uint32_t id, ++ struct usb_redir_alt_setting_status_header *alt_setting_status); ++static void usbredir_iso_stream_status(void *priv, uint32_t id, ++ struct usb_redir_iso_stream_status_header *iso_stream_status); ++static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, ++ struct usb_redir_interrupt_receiving_status_header ++ *interrupt_receiving_status); ++static void usbredir_bulk_streams_status(void *priv, uint32_t id, ++ struct usb_redir_bulk_streams_status_header *bulk_streams_status); ++static void usbredir_control_packet(void *priv, uint32_t id, ++ struct usb_redir_control_packet_header *control_packet, ++ uint8_t *data, int data_len); ++static void usbredir_bulk_packet(void *priv, uint32_t id, ++ struct usb_redir_bulk_packet_header *bulk_packet, ++ uint8_t *data, int data_len); ++static void usbredir_iso_packet(void *priv, uint32_t id, ++ struct usb_redir_iso_packet_header *iso_packet, ++ uint8_t *data, int data_len); ++static void usbredir_interrupt_packet(void *priv, uint32_t id, ++ struct usb_redir_interrupt_packet_header *interrupt_header, ++ uint8_t *data, int data_len); ++ ++static int usbredir_handle_status(USBRedirDevice *dev, ++ int status, int actual_len); ++ ++#define VERSION "qemu usb-redir guest " QEMU_VERSION ++ ++/* ++ * Logging stuff ++ */ ++ ++#define ERROR(...) \ ++ do { \ ++ if (dev->debug >= usbredirparser_error) { \ ++ error_report("usb-redir error: " __VA_ARGS__); \ ++ } \ ++ } while (0) ++#define WARNING(...) \ ++ do { \ ++ if (dev->debug >= usbredirparser_warning) { \ ++ error_report("usb-redir warning: " __VA_ARGS__); \ ++ } \ ++ } while (0) ++#define INFO(...) \ ++ do { \ ++ if (dev->debug >= usbredirparser_info) { \ ++ error_report("usb-redir: " __VA_ARGS__); \ ++ } \ ++ } while (0) ++#define DPRINTF(...) \ ++ do { \ ++ if (dev->debug >= usbredirparser_debug) { \ ++ error_report("usb-redir: " __VA_ARGS__); \ ++ } \ ++ } while (0) ++#define DPRINTF2(...) \ ++ do { \ ++ if (dev->debug >= usbredirparser_debug_data) { \ ++ error_report("usb-redir: " __VA_ARGS__); \ ++ } \ ++ } while (0) ++ ++static void usbredir_log(void *priv, int level, const char *msg) ++{ ++ USBRedirDevice *dev = priv; ++ ++ if (dev->debug < level) { ++ return; ++ } ++ ++ error_report("%s\n", msg); ++} ++ ++static void usbredir_log_data(USBRedirDevice *dev, const char *desc, ++ const uint8_t *data, int len) ++{ ++ int i, j, n; ++ ++ if (dev->debug < usbredirparser_debug_data) { ++ return; ++ } ++ ++ for (i = 0; i < len; i += j) { ++ char buf[128]; ++ ++ n = sprintf(buf, "%s", desc); ++ for (j = 0; j < 8 && i + j < len; j++) { ++ n += sprintf(buf + n, " %02X", data[i + j]); ++ } ++ error_report("%s\n", buf); ++ } ++} ++ ++/* ++ * usbredirparser io functions ++ */ ++ ++static int usbredir_read(void *priv, uint8_t *data, int count) ++{ ++ USBRedirDevice *dev = priv; ++ ++ if (dev->read_buf_size < count) { ++ count = dev->read_buf_size; ++ } ++ ++ memcpy(data, dev->read_buf, count); ++ ++ dev->read_buf_size -= count; ++ if (dev->read_buf_size) { ++ dev->read_buf += count; ++ } else { ++ dev->read_buf = NULL; ++ } ++ ++ return count; ++} ++ ++static int usbredir_write(void *priv, uint8_t *data, int count) ++{ ++ USBRedirDevice *dev = priv; ++ ++ return qemu_chr_write(dev->cs, data, count); ++} ++ ++/* ++ * Async and buffered packets helpers ++ */ ++ ++static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) ++{ ++ AsyncURB *aurb = (AsyncURB *) qemu_mallocz(sizeof(AsyncURB)); ++ aurb->dev = dev; ++ aurb->packet = p; ++ aurb->packet_id = dev->packet_id; ++ QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); ++ dev->packet_id++; ++ ++ return aurb; ++} ++ ++static void async_free(USBRedirDevice *dev, AsyncURB *aurb) ++{ ++ QTAILQ_REMOVE(&dev->asyncq, aurb, next); ++ qemu_free(aurb); ++} ++ ++static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id) ++{ ++ AsyncURB *aurb; ++ ++ QTAILQ_FOREACH(aurb, &dev->asyncq, next) { ++ if (aurb->packet_id == packet_id) { ++ return aurb; ++ } ++ } ++ ERROR("could not find async urb for packet_id %u\n", packet_id); ++ return NULL; ++} ++ ++static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ AsyncURB *aurb; ++ ++ QTAILQ_FOREACH(aurb, &dev->asyncq, next) { ++ if (p != aurb->packet) { ++ continue; ++ } ++ ++ DPRINTF("async cancel id %u\n", aurb->packet_id); ++ usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id); ++ usbredirparser_do_write(dev->parser); ++ ++ /* Mark it as dead */ ++ aurb->packet = NULL; ++ break; ++ } ++} ++ ++static struct buf_packet *bufp_alloc(USBRedirDevice *dev, ++ uint8_t *data, int len, int status, uint8_t ep) ++{ ++ struct buf_packet *bufp = qemu_malloc(sizeof(struct buf_packet)); ++ bufp->data = data; ++ bufp->len = len; ++ bufp->status = status; ++ QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); ++ return bufp; ++} ++ ++static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, ++ uint8_t ep) ++{ ++ QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); ++ free(bufp->data); ++ qemu_free(bufp); ++} ++ ++static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep) ++{ ++ struct buf_packet *buf, *buf_next; ++ ++ QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) { ++ bufp_free(dev, buf, ep); ++ } ++} ++ ++/* ++ * USBDevice callbacks ++ */ ++ ++static void usbredir_handle_reset(USBDevice *udev) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ ++ DPRINTF("reset device\n"); ++ usbredirparser_send_reset(dev->parser); ++ usbredirparser_do_write(dev->parser); ++} ++ ++static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, ++ uint8_t ep) ++{ ++ int status, len; ++ ++ if (!dev->endpoint[EP2I(ep)].iso_started && ++ !dev->endpoint[EP2I(ep)].iso_error) { ++ struct usb_redir_start_iso_stream_header start_iso = { ++ .endpoint = ep, ++ /* TODO maybe do something with these depending on ep interval? */ ++ .pkts_per_urb = 32, ++ .no_urbs = 3, ++ }; ++ /* No id, we look at the ep when receiving a status back */ ++ usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso); ++ usbredirparser_do_write(dev->parser); ++ DPRINTF("iso stream started ep %02X\n", ep); ++ dev->endpoint[EP2I(ep)].iso_started = 1; ++ } ++ ++ if (ep & USB_DIR_IN) { ++ struct buf_packet *isop; ++ ++ isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); ++ if (isop == NULL) { ++ DPRINTF2("iso-token-in ep %02X, no isop\n", ep); ++ /* Check iso_error for stream errors, otherwise its an underrun */ ++ status = dev->endpoint[EP2I(ep)].iso_error; ++ dev->endpoint[EP2I(ep)].iso_error = 0; ++ return usbredir_handle_status(dev, status, 0); ++ } ++ DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status, ++ isop->len); ++ ++ status = isop->status; ++ if (status != usb_redir_success) { ++ bufp_free(dev, isop, ep); ++ return usbredir_handle_status(dev, status, 0); ++ } ++ ++ len = isop->len; ++ if (len > p->len) { ++ ERROR("received iso data is larger then packet ep %02X\n", ep); ++ bufp_free(dev, isop, ep); ++ return USB_RET_NAK; ++ } ++ memcpy(p->data, isop->data, len); ++ bufp_free(dev, isop, ep); ++ return len; ++ } else { ++ /* If the stream was not started because of a pending error don't ++ send the packet to the usb-host */ ++ if (dev->endpoint[EP2I(ep)].iso_started) { ++ struct usb_redir_iso_packet_header iso_packet = { ++ .endpoint = ep, ++ .length = p->len ++ }; ++ /* No id, we look at the ep when receiving a status back */ ++ usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, ++ p->data, p->len); ++ usbredirparser_do_write(dev->parser); ++ } ++ status = dev->endpoint[EP2I(ep)].iso_error; ++ dev->endpoint[EP2I(ep)].iso_error = 0; ++ DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status, ++ p->len); ++ return usbredir_handle_status(dev, status, p->len); ++ } ++} ++ ++static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) ++{ ++ struct usb_redir_stop_iso_stream_header stop_iso_stream = { ++ .endpoint = ep ++ }; ++ if (dev->endpoint[EP2I(ep)].iso_started) { ++ usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream); ++ DPRINTF("iso stream stopped ep %02X\n", ep); ++ dev->endpoint[EP2I(ep)].iso_started = 0; ++ } ++ usbredir_free_bufpq(dev, ep); ++} ++ ++static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, ++ uint8_t ep) ++{ ++ AsyncURB *aurb = async_alloc(dev, p); ++ struct usb_redir_bulk_packet_header bulk_packet; ++ ++ DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id); ++ ++ bulk_packet.endpoint = ep; ++ bulk_packet.length = p->len; ++ bulk_packet.stream_id = 0; ++ aurb->bulk_packet = bulk_packet; ++ ++ if (ep & USB_DIR_IN) { ++ usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, ++ &bulk_packet, NULL, 0); ++ } else { ++ usbredir_log_data(dev, "bulk data out:", p->data, p->len); ++ usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, ++ &bulk_packet, p->data, p->len); ++ } ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++} ++ ++static int usbredir_handle_interrupt_data(USBRedirDevice *dev, ++ USBPacket *p, uint8_t ep) ++{ ++ if (ep & USB_DIR_IN) { ++ /* Input interrupt endpoint, buffered packet input */ ++ struct buf_packet *intp; ++ int status, len; ++ ++ if (!dev->endpoint[EP2I(ep)].interrupt_started && ++ !dev->endpoint[EP2I(ep)].interrupt_error) { ++ struct usb_redir_start_interrupt_receiving_header start_int = { ++ .endpoint = ep, ++ }; ++ /* No id, we look at the ep when receiving a status back */ ++ usbredirparser_send_start_interrupt_receiving(dev->parser, 0, ++ &start_int); ++ usbredirparser_do_write(dev->parser); ++ DPRINTF("interrupt recv started ep %02X\n", ep); ++ dev->endpoint[EP2I(ep)].interrupt_started = 1; ++ } ++ ++ intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); ++ if (intp == NULL) { ++ DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep); ++ /* Check interrupt_error for stream errors */ ++ status = dev->endpoint[EP2I(ep)].interrupt_error; ++ dev->endpoint[EP2I(ep)].interrupt_error = 0; ++ return usbredir_handle_status(dev, status, 0); ++ } ++ DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, ++ intp->status, intp->len); ++ ++ status = intp->status; ++ if (status != usb_redir_success) { ++ bufp_free(dev, intp, ep); ++ return usbredir_handle_status(dev, status, 0); ++ } ++ ++ len = intp->len; ++ if (len > p->len) { ++ ERROR("received int data is larger then packet ep %02X\n", ep); ++ bufp_free(dev, intp, ep); ++ return USB_RET_NAK; ++ } ++ memcpy(p->data, intp->data, len); ++ bufp_free(dev, intp, ep); ++ return len; ++ } else { ++ /* Output interrupt endpoint, normal async operation */ ++ AsyncURB *aurb = async_alloc(dev, p); ++ struct usb_redir_interrupt_packet_header interrupt_packet; ++ ++ DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len, ++ aurb->packet_id); ++ ++ interrupt_packet.endpoint = ep; ++ interrupt_packet.length = p->len; ++ aurb->interrupt_packet = interrupt_packet; ++ ++ usbredir_log_data(dev, "interrupt data out:", p->data, p->len); ++ usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, ++ &interrupt_packet, p->data, p->len); ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++ } ++} ++ ++static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev, ++ uint8_t ep) ++{ ++ struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = { ++ .endpoint = ep ++ }; ++ if (dev->endpoint[EP2I(ep)].interrupt_started) { ++ usbredirparser_send_stop_interrupt_receiving(dev->parser, 0, ++ &stop_interrupt_recv); ++ DPRINTF("interrupt recv stopped ep %02X\n", ep); ++ dev->endpoint[EP2I(ep)].interrupt_started = 0; ++ } ++ usbredir_free_bufpq(dev, ep); ++} ++ ++static int usbredir_handle_data(USBDevice *udev, USBPacket *p) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ uint8_t ep; ++ ++ ep = p->devep; ++ if (p->pid == USB_TOKEN_IN) { ++ ep |= USB_DIR_IN; ++ } ++ ++ switch (dev->endpoint[EP2I(ep)].type) { ++ case USB_ENDPOINT_XFER_CONTROL: ++ ERROR("handle_data called for control transfer on ep %02X\n", ep); ++ return USB_RET_NAK; ++ case USB_ENDPOINT_XFER_ISOC: ++ return usbredir_handle_iso_data(dev, p, ep); ++ case USB_ENDPOINT_XFER_BULK: ++ return usbredir_handle_bulk_data(dev, p, ep);; ++ case USB_ENDPOINT_XFER_INT: ++ return usbredir_handle_interrupt_data(dev, p, ep);; ++ default: ++ ERROR("handle_data ep %02X has unknown type %d\n", ep, ++ dev->endpoint[EP2I(ep)].type); ++ return USB_RET_NAK; ++ } ++} ++ ++static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, ++ int config) ++{ ++ struct usb_redir_set_configuration_header set_config; ++ AsyncURB *aurb = async_alloc(dev, p); ++ int i; ++ ++ DPRINTF("set config %d id %u\n", config, aurb->packet_id); ++ ++ for (i = 0; i < MAX_ENDPOINTS; i++) { ++ switch (dev->endpoint[i].type) { ++ case USB_ENDPOINT_XFER_ISOC: ++ usbredir_stop_iso_stream(dev, I2EP(i)); ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ if (i & 0x10) { ++ usbredir_stop_interrupt_receiving(dev, I2EP(i)); ++ } ++ break; ++ } ++ usbredir_free_bufpq(dev, I2EP(i)); ++ } ++ ++ set_config.configuration = config; ++ usbredirparser_send_set_configuration(dev->parser, aurb->packet_id, ++ &set_config); ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++} ++ ++static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) ++{ ++ AsyncURB *aurb = async_alloc(dev, p); ++ ++ DPRINTF("get config id %u\n", aurb->packet_id); ++ ++ aurb->get = 1; ++ usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++} ++ ++static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, ++ int interface, int alt) ++{ ++ struct usb_redir_set_alt_setting_header set_alt; ++ AsyncURB *aurb = async_alloc(dev, p); ++ int i; ++ ++ DPRINTF("set interface %d alt %d id %u\n", interface, alt, ++ aurb->packet_id); ++ ++ for (i = 0; i < MAX_ENDPOINTS; i++) { ++ if (dev->endpoint[i].interface == interface) { ++ switch (dev->endpoint[i].type) { ++ case USB_ENDPOINT_XFER_ISOC: ++ usbredir_stop_iso_stream(dev, I2EP(i)); ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ if (i & 0x10) { ++ usbredir_stop_interrupt_receiving(dev, I2EP(i)); ++ } ++ break; ++ } ++ usbredir_free_bufpq(dev, I2EP(i)); ++ } ++ } ++ ++ set_alt.interface = interface; ++ set_alt.alt = alt; ++ usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id, ++ &set_alt); ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++} ++ ++static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, ++ int interface) ++{ ++ struct usb_redir_get_alt_setting_header get_alt; ++ AsyncURB *aurb = async_alloc(dev, p); ++ ++ DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); ++ ++ get_alt.interface = interface; ++ aurb->get = 1; ++ usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, ++ &get_alt); ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++} ++ ++static int usbredir_handle_control(USBDevice *udev, USBPacket *p, ++ int request, int value, int index, int length, uint8_t *data) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ struct usb_redir_control_packet_header control_packet; ++ AsyncURB *aurb; ++ ++ /* Special cases for certain standard device requests */ ++ switch (request) { ++ case DeviceOutRequest | USB_REQ_SET_ADDRESS: ++ DPRINTF("set address %d\n", value); ++ dev->dev.addr = value; ++ return 0; ++ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: ++ return usbredir_set_config(dev, p, value & 0xff); ++ case DeviceRequest | USB_REQ_GET_CONFIGURATION: ++ return usbredir_get_config(dev, p); ++ case InterfaceOutRequest | USB_REQ_SET_INTERFACE: ++ return usbredir_set_interface(dev, p, index, value); ++ case InterfaceRequest | USB_REQ_GET_INTERFACE: ++ return usbredir_get_interface(dev, p, index); ++ } ++ ++ /* "Normal" ctrl requests */ ++ aurb = async_alloc(dev, p); ++ ++ /* Note request is (bRequestType << 8) | bRequest */ ++ DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n", ++ request >> 8, request & 0xff, value, index, length, ++ aurb->packet_id); ++ ++ control_packet.request = request & 0xFF; ++ control_packet.requesttype = request >> 8; ++ control_packet.endpoint = control_packet.requesttype & USB_DIR_IN; ++ control_packet.value = value; ++ control_packet.index = index; ++ control_packet.length = length; ++ aurb->control_packet = control_packet; ++ ++ if (control_packet.requesttype & USB_DIR_IN) { ++ usbredirparser_send_control_packet(dev->parser, aurb->packet_id, ++ &control_packet, NULL, 0); ++ } else { ++ usbredir_log_data(dev, "ctrl data out:", data, length); ++ usbredirparser_send_control_packet(dev->parser, aurb->packet_id, ++ &control_packet, data, length); ++ } ++ usbredirparser_do_write(dev->parser); ++ return USB_RET_ASYNC; ++} ++ ++/* ++ * Close events can be triggered by usbredirparser_do_write which gets called ++ * from within the USBDevice data / control packet callbacks and doing a ++ * usb_detach from within these callbacks is not a good idea. ++ * ++ * So we use a bh handler to take care of close events. We also handle ++ * open events from this callback to make sure that a close directly followed ++ * by an open gets handled in the right order. ++ */ ++static void usbredir_open_close_bh(void *opaque) ++{ ++ USBRedirDevice *dev = opaque; ++ ++ usbredir_device_disconnect(dev); ++ ++ if (dev->parser) { ++ usbredirparser_destroy(dev->parser); ++ dev->parser = NULL; ++ } ++ ++ if (dev->cs->opened) { ++ dev->parser = qemu_oom_check(usbredirparser_create()); ++ dev->parser->priv = dev; ++ dev->parser->log_func = usbredir_log; ++ dev->parser->read_func = usbredir_read; ++ dev->parser->write_func = usbredir_write; ++ dev->parser->device_connect_func = usbredir_device_connect; ++ dev->parser->device_disconnect_func = usbredir_device_disconnect; ++ dev->parser->interface_info_func = usbredir_interface_info; ++ dev->parser->ep_info_func = usbredir_ep_info; ++ dev->parser->configuration_status_func = usbredir_configuration_status; ++ dev->parser->alt_setting_status_func = usbredir_alt_setting_status; ++ dev->parser->iso_stream_status_func = usbredir_iso_stream_status; ++ dev->parser->interrupt_receiving_status_func = ++ usbredir_interrupt_receiving_status; ++ dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; ++ dev->parser->control_packet_func = usbredir_control_packet; ++ dev->parser->bulk_packet_func = usbredir_bulk_packet; ++ dev->parser->iso_packet_func = usbredir_iso_packet; ++ dev->parser->interrupt_packet_func = usbredir_interrupt_packet; ++ dev->read_buf = NULL; ++ dev->read_buf_size = 0; ++ usbredirparser_init(dev->parser, VERSION, NULL, 0, 0); ++ usbredirparser_do_write(dev->parser); ++ } ++} ++ ++static void usbredir_do_attach(void *opaque) ++{ ++ USBRedirDevice *dev = opaque; ++ ++ usb_device_attach(&dev->dev); ++} ++ ++/* ++ * chardev callbacks ++ */ ++ ++static int usbredir_chardev_can_read(void *opaque) ++{ ++ USBRedirDevice *dev = opaque; ++ ++ if (dev->parser) { ++ /* usbredir_parser_do_read will consume *all* data we give it */ ++ return 1024 * 1024; ++ } else { ++ /* usbredir_open_close_bh hasn't handled the open event yet */ ++ return 0; ++ } ++} ++ ++static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) ++{ ++ USBRedirDevice *dev = opaque; ++ ++ /* No recursion allowed! */ ++ assert(dev->read_buf == NULL); ++ ++ dev->read_buf = buf; ++ dev->read_buf_size = size; ++ ++ usbredirparser_do_read(dev->parser); ++ /* Send any acks, etc. which may be queued now */ ++ usbredirparser_do_write(dev->parser); ++} ++ ++static void usbredir_chardev_event(void *opaque, int event) ++{ ++ USBRedirDevice *dev = opaque; ++ ++ switch (event) { ++ case CHR_EVENT_OPENED: ++ case CHR_EVENT_CLOSED: ++ qemu_bh_schedule(dev->open_close_bh); ++ break; ++ } ++} ++ ++/* ++ * init + destroy ++ */ ++ ++static int usbredir_initfn(USBDevice *udev) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ int i; ++ ++ if (dev->cs == NULL) { ++ qerror_report(QERR_MISSING_PARAMETER, "chardev"); ++ return -1; ++ } ++ ++ dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); ++ dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); ++ ++ QTAILQ_INIT(&dev->asyncq); ++ for (i = 0; i < MAX_ENDPOINTS; i++) { ++ QTAILQ_INIT(&dev->endpoint[i].bufpq); ++ } ++ ++ /* We'll do the attach once we receive the speed from the usb-host */ ++ udev->auto_attach = 0; ++ ++ qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, ++ usbredir_chardev_read, usbredir_chardev_event, dev); ++ ++ return 0; ++} ++ ++static void usbredir_cleanup_device_queues(USBRedirDevice *dev) ++{ ++ AsyncURB *aurb, *next_aurb; ++ int i; ++ ++ QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) { ++ async_free(dev, aurb); ++ } ++ for (i = 0; i < MAX_ENDPOINTS; i++) { ++ usbredir_free_bufpq(dev, I2EP(i)); ++ } ++} ++ ++static void usbredir_handle_destroy(USBDevice *udev) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ ++ qemu_chr_close(dev->cs); ++ /* Note must be done after qemu_chr_close, as that causes a close event */ ++ qemu_bh_delete(dev->open_close_bh); ++ ++ qemu_del_timer(dev->attach_timer); ++ qemu_free_timer(dev->attach_timer); ++ ++ usbredir_cleanup_device_queues(dev); ++ ++ if (dev->parser) { ++ usbredirparser_destroy(dev->parser); ++ } ++} ++ ++/* ++ * usbredirparser packet complete callbacks ++ */ ++ ++static int usbredir_handle_status(USBRedirDevice *dev, ++ int status, int actual_len) ++{ ++ switch (status) { ++ case usb_redir_success: ++ return actual_len; ++ case usb_redir_stall: ++ return USB_RET_STALL; ++ case usb_redir_cancelled: ++ WARNING("returning cancelled packet to HC?\n"); ++ case usb_redir_inval: ++ case usb_redir_ioerror: ++ case usb_redir_timeout: ++ default: ++ return USB_RET_NAK; ++ } ++} ++ ++static void usbredir_device_connect(void *priv, ++ struct usb_redir_device_connect_header *device_connect) ++{ ++ USBRedirDevice *dev = priv; ++ ++ switch (device_connect->speed) { ++ case usb_redir_speed_low: ++ DPRINTF("attaching low speed device\n"); ++ dev->dev.speed = USB_SPEED_LOW; ++ break; ++ case usb_redir_speed_full: ++ DPRINTF("attaching full speed device\n"); ++ dev->dev.speed = USB_SPEED_FULL; ++ break; ++ case usb_redir_speed_high: ++ DPRINTF("attaching high speed device\n"); ++ dev->dev.speed = USB_SPEED_HIGH; ++ break; ++ case usb_redir_speed_super: ++ DPRINTF("attaching super speed device\n"); ++ dev->dev.speed = USB_SPEED_SUPER; ++ break; ++ default: ++ DPRINTF("attaching unknown speed device, assuming full speed\n"); ++ dev->dev.speed = USB_SPEED_FULL; ++ } ++ dev->dev.speedmask = (1 << dev->dev.speed); ++ qemu_mod_timer(dev->attach_timer, dev->next_attach_time); ++} ++ ++static void usbredir_device_disconnect(void *priv) ++{ ++ USBRedirDevice *dev = priv; ++ ++ /* Stop any pending attaches */ ++ qemu_del_timer(dev->attach_timer); ++ ++ if (dev->dev.attached) { ++ usb_device_detach(&dev->dev); ++ usbredir_cleanup_device_queues(dev); ++ /* ++ * Delay next usb device attach to give the guest a chance to see ++ * see the detach / attach in case of quick close / open succession ++ */ ++ dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200; ++ } ++} ++ ++static void usbredir_interface_info(void *priv, ++ struct usb_redir_interface_info_header *interface_info) ++{ ++ /* The intention is to allow specifying acceptable interface classes ++ for redirection on the cmdline and in the future verify this here, ++ and disconnect (or never connect) the device if a not accepted ++ interface class is detected */ ++} ++ ++static void usbredir_ep_info(void *priv, ++ struct usb_redir_ep_info_header *ep_info) ++{ ++ USBRedirDevice *dev = priv; ++ int i; ++ ++ for (i = 0; i < MAX_ENDPOINTS; i++) { ++ dev->endpoint[i].type = ep_info->type[i]; ++ dev->endpoint[i].interval = ep_info->interval[i]; ++ dev->endpoint[i].interface = ep_info->interface[i]; ++ if (dev->endpoint[i].type != usb_redir_type_invalid) { ++ DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i), ++ dev->endpoint[i].type, dev->endpoint[i].interface); ++ } ++ } ++} ++ ++static void usbredir_configuration_status(void *priv, uint32_t id, ++ struct usb_redir_configuration_status_header *config_status) ++{ ++ USBRedirDevice *dev = priv; ++ AsyncURB *aurb; ++ int len = 0; ++ ++ DPRINTF("set config status %d config %d id %u\n", config_status->status, ++ config_status->configuration, id); ++ ++ aurb = async_find(dev, id); ++ if (!aurb) { ++ return; ++ } ++ if (aurb->packet) { ++ if (aurb->get) { ++ dev->dev.data_buf[0] = config_status->configuration; ++ len = 1; ++ } ++ aurb->packet->len = ++ usbredir_handle_status(dev, config_status->status, len); ++ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); ++ } ++ async_free(dev, aurb); ++} ++ ++static void usbredir_alt_setting_status(void *priv, uint32_t id, ++ struct usb_redir_alt_setting_status_header *alt_setting_status) ++{ ++ USBRedirDevice *dev = priv; ++ AsyncURB *aurb; ++ int len = 0; ++ ++ DPRINTF("alt status %d intf %d alt %d id: %u\n", ++ alt_setting_status->status, ++ alt_setting_status->interface, ++ alt_setting_status->alt, id); ++ ++ aurb = async_find(dev, id); ++ if (!aurb) { ++ return; ++ } ++ if (aurb->packet) { ++ if (aurb->get) { ++ dev->dev.data_buf[0] = alt_setting_status->alt; ++ len = 1; ++ } ++ aurb->packet->len = ++ usbredir_handle_status(dev, alt_setting_status->status, len); ++ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); ++ } ++ async_free(dev, aurb); ++} ++ ++static void usbredir_iso_stream_status(void *priv, uint32_t id, ++ struct usb_redir_iso_stream_status_header *iso_stream_status) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t ep = iso_stream_status->endpoint; ++ ++ DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status, ++ ep, id); ++ ++ dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status; ++ if (iso_stream_status->status == usb_redir_stall) { ++ DPRINTF("iso stream stopped by peer ep %02X\n", ep); ++ dev->endpoint[EP2I(ep)].iso_started = 0; ++ } ++} ++ ++static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, ++ struct usb_redir_interrupt_receiving_status_header ++ *interrupt_receiving_status) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t ep = interrupt_receiving_status->endpoint; ++ ++ DPRINTF("interrupt recv status %d ep %02X id %u\n", ++ interrupt_receiving_status->status, ep, id); ++ ++ dev->endpoint[EP2I(ep)].interrupt_error = ++ interrupt_receiving_status->status; ++ if (interrupt_receiving_status->status == usb_redir_stall) { ++ DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep); ++ dev->endpoint[EP2I(ep)].interrupt_started = 0; ++ } ++} ++ ++static void usbredir_bulk_streams_status(void *priv, uint32_t id, ++ struct usb_redir_bulk_streams_status_header *bulk_streams_status) ++{ ++} ++ ++static void usbredir_control_packet(void *priv, uint32_t id, ++ struct usb_redir_control_packet_header *control_packet, ++ uint8_t *data, int data_len) ++{ ++ USBRedirDevice *dev = priv; ++ int len = control_packet->length; ++ AsyncURB *aurb; ++ ++ DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, ++ len, id); ++ ++ aurb = async_find(dev, id); ++ if (!aurb) { ++ free(data); ++ return; ++ } ++ ++ aurb->control_packet.status = control_packet->status; ++ aurb->control_packet.length = control_packet->length; ++ if (memcmp(&aurb->control_packet, control_packet, ++ sizeof(*control_packet))) { ++ ERROR("return control packet mismatch, please report this!\n"); ++ len = USB_RET_NAK; ++ } ++ ++ if (aurb->packet) { ++ len = usbredir_handle_status(dev, control_packet->status, len); ++ if (len > 0) { ++ usbredir_log_data(dev, "ctrl data in:", data, data_len); ++ if (data_len <= sizeof(dev->dev.data_buf)) { ++ memcpy(dev->dev.data_buf, data, data_len); ++ } else { ++ ERROR("ctrl buffer too small (%d > %zu)\n", ++ data_len, sizeof(dev->dev.data_buf)); ++ len = USB_RET_STALL; ++ } ++ } ++ aurb->packet->len = len; ++ usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); ++ } ++ async_free(dev, aurb); ++ free(data); ++} ++ ++static void usbredir_bulk_packet(void *priv, uint32_t id, ++ struct usb_redir_bulk_packet_header *bulk_packet, ++ uint8_t *data, int data_len) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t ep = bulk_packet->endpoint; ++ int len = bulk_packet->length; ++ AsyncURB *aurb; ++ ++ DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, ++ ep, len, id); ++ ++ aurb = async_find(dev, id); ++ if (!aurb) { ++ free(data); ++ return; ++ } ++ ++ if (aurb->bulk_packet.endpoint != bulk_packet->endpoint || ++ aurb->bulk_packet.stream_id != bulk_packet->stream_id) { ++ ERROR("return bulk packet mismatch, please report this!\n"); ++ len = USB_RET_NAK; ++ } ++ ++ if (aurb->packet) { ++ len = usbredir_handle_status(dev, bulk_packet->status, len); ++ if (len > 0) { ++ usbredir_log_data(dev, "bulk data in:", data, data_len); ++ if (data_len <= aurb->packet->len) { ++ memcpy(aurb->packet->data, data, data_len); ++ } else { ++ ERROR("bulk buffer too small (%d > %d)\n", data_len, ++ aurb->packet->len); ++ len = USB_RET_STALL; ++ } ++ } ++ aurb->packet->len = len; ++ usb_packet_complete(&dev->dev, aurb->packet); ++ } ++ async_free(dev, aurb); ++ free(data); ++} ++ ++static void usbredir_iso_packet(void *priv, uint32_t id, ++ struct usb_redir_iso_packet_header *iso_packet, ++ uint8_t *data, int data_len) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t ep = iso_packet->endpoint; ++ ++ DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep, ++ data_len, id); ++ ++ if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { ++ ERROR("received iso packet for non iso endpoint %02X\n", ep); ++ free(data); ++ return; ++ } ++ ++ if (dev->endpoint[EP2I(ep)].iso_started == 0) { ++ DPRINTF("received iso packet for non started stream ep %02X\n", ep); ++ free(data); ++ return; ++ } ++ ++ /* bufp_alloc also adds the packet to the ep queue */ ++ bufp_alloc(dev, data, data_len, iso_packet->status, ep); ++} ++ ++static void usbredir_interrupt_packet(void *priv, uint32_t id, ++ struct usb_redir_interrupt_packet_header *interrupt_packet, ++ uint8_t *data, int data_len) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t ep = interrupt_packet->endpoint; ++ ++ DPRINTF("interrupt-in status %d ep %02X len %d id %u\n", ++ interrupt_packet->status, ep, data_len, id); ++ ++ if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { ++ ERROR("received int packet for non interrupt endpoint %02X\n", ep); ++ free(data); ++ return; ++ } ++ ++ if (ep & USB_DIR_IN) { ++ if (dev->endpoint[EP2I(ep)].interrupt_started == 0) { ++ DPRINTF("received int packet while not started ep %02X\n", ep); ++ free(data); ++ return; ++ } ++ ++ /* bufp_alloc also adds the packet to the ep queue */ ++ bufp_alloc(dev, data, data_len, interrupt_packet->status, ep); ++ } else { ++ int len = interrupt_packet->length; ++ ++ AsyncURB *aurb = async_find(dev, id); ++ if (!aurb) { ++ return; ++ } ++ ++ if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) { ++ ERROR("return int packet mismatch, please report this!\n"); ++ len = USB_RET_NAK; ++ } ++ ++ if (aurb->packet) { ++ aurb->packet->len = usbredir_handle_status(dev, ++ interrupt_packet->status, len); ++ usb_packet_complete(&dev->dev, aurb->packet); ++ } ++ async_free(dev, aurb); ++ } ++} ++ ++static struct USBDeviceInfo usbredir_dev_info = { ++ .product_desc = "USB Redirection Device", ++ .qdev.name = "usb-redir", ++ .qdev.size = sizeof(USBRedirDevice), ++ .init = usbredir_initfn, ++ .handle_destroy = usbredir_handle_destroy, ++ .handle_packet = usb_generic_handle_packet, ++ .cancel_packet = usbredir_cancel_packet, ++ .handle_reset = usbredir_handle_reset, ++ .handle_data = usbredir_handle_data, ++ .handle_control = usbredir_handle_control, ++ .qdev.props = (Property[]) { ++ DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), ++ DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), ++ DEFINE_PROP_END_OF_LIST(), ++ }, ++}; ++ ++static void usbredir_register_devices(void) ++{ ++ usb_qdev_register(&usbredir_dev_info); ++} ++device_init(usbredir_register_devices); +-- +1.7.5.1 + diff --git a/0025-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch b/0025-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch new file mode 100644 index 0000000..2632655 --- /dev/null +++ b/0025-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch @@ -0,0 +1,57 @@ +From d24514c807c4cddfe5f919bd01cb1a9cd3f21b10 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 21:57:47 +0100 +Subject: [PATCH 25/35] char: Split out tcp socket close code in a separate + function + +Signed-off-by: Amit Shah +--- + qemu-char.c | 25 ++++++++++++++++--------- + 1 files changed, 16 insertions(+), 9 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index fb13b28..75efa60 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -1917,6 +1917,21 @@ typedef struct { + + static void tcp_chr_accept(void *opaque); + ++static void tcp_closed(void *opaque) ++{ ++ CharDriverState *chr = opaque; ++ TCPCharDriver *s = chr->opaque; ++ ++ s->connected = 0; ++ if (s->listen_fd >= 0) { ++ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); ++ } ++ qemu_set_fd_handler(s->fd, NULL, NULL, NULL); ++ closesocket(s->fd); ++ s->fd = -1; ++ qemu_chr_event(chr, CHR_EVENT_CLOSED); ++} ++ + static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + TCPCharDriver *s = chr->opaque; +@@ -2067,15 +2082,7 @@ static void tcp_chr_read(void *opaque) + len = s->max_size; + size = tcp_chr_recv(chr, (void *)buf, len); + if (size == 0) { +- /* connection closed */ +- s->connected = 0; +- if (s->listen_fd >= 0) { +- qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); +- } +- qemu_set_fd_handler(s->fd, NULL, NULL, NULL); +- closesocket(s->fd); +- s->fd = -1; +- qemu_chr_event(chr, CHR_EVENT_CLOSED); ++ tcp_closed(chr); + } else if (size > 0) { + if (s->do_telnetopt) + tcp_chr_process_IAC_bytes(chr, s, buf, &size); +-- +1.7.5.1 + diff --git a/0026-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch b/0026-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch new file mode 100644 index 0000000..7433073 --- /dev/null +++ b/0026-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch @@ -0,0 +1,882 @@ +From 7a78607683a3c3a25ab715d6a5e137be9cf29e66 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 20:31:45 +0100 +Subject: [PATCH 26/35] char: Add a QemuChrHandlers struct to initialise + chardev handlers + +Instead of passing each handler in the qemu_add_handlers() function, +create a struct of handlers that can be passed to the function instead. + +Signed-off-by: Amit Shah +--- + gdbstub.c | 9 +++++++-- + hw/ccid-card-passthru.c | 11 +++++++---- + hw/debugcon.c | 2 +- + hw/escc.c | 9 +++++++-- + hw/etraxfs_ser.c | 13 +++++++++---- + hw/grlib_apbuart.c | 12 +++++++----- + hw/ivshmem.c | 28 ++++++++++++++++++++++------ + hw/lm32_juart.c | 8 +++++++- + hw/lm32_uart.c | 8 +++++++- + hw/mcf_uart.c | 9 +++++++-- + hw/milkymist-uart.c | 8 +++++++- + hw/pl011.c | 9 +++++++-- + hw/pxa2xx.c | 13 +++++++++---- + hw/serial.c | 9 +++++++-- + hw/sh_serial.c | 12 +++++++++--- + hw/spapr_vty.c | 8 ++++++-- + hw/strongarm.c | 12 +++++++----- + hw/syborg_serial.c | 9 +++++++-- + hw/usb-serial.c | 9 +++++++-- + hw/virtio-console.c | 11 ++++++++--- + hw/xen_console.c | 16 +++++++++++----- + hw/xilinx_uartlite.c | 11 +++++++++-- + monitor.c | 18 ++++++++++++++---- + net/slirp.c | 8 ++++++-- + qemu-char.c | 32 ++++++++++++++++++++++---------- + qemu-char.h | 13 +++++++++---- + usb-redir.c | 9 +++++++-- + 27 files changed, 233 insertions(+), 83 deletions(-) + +diff --git a/gdbstub.c b/gdbstub.c +index c085a5a..0ff61cb 100644 +--- a/gdbstub.c ++++ b/gdbstub.c +@@ -2739,6 +2739,12 @@ static void gdb_sigterm_handler(int signal) + } + #endif + ++static const QemuChrHandlers gdb_handlers = { ++ .fd_can_read = gdb_chr_can_receive, ++ .fd_read = gdb_chr_receive, ++ .fd_event = gdb_chr_event, ++}; ++ + int gdbserver_start(const char *device) + { + GDBState *s; +@@ -2768,8 +2774,7 @@ int gdbserver_start(const char *device) + if (!chr) + return -1; + +- qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive, +- gdb_chr_event, NULL); ++ qemu_chr_add_handlers(chr, &gdb_handlers, NULL); + } + + s = gdbserver_state; +diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c +index 28eb9d1..1dee6f7 100644 +--- a/hw/ccid-card-passthru.c ++++ b/hw/ccid-card-passthru.c +@@ -273,6 +273,12 @@ static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) + return card->atr; + } + ++static const QemuChrHandlers passthru_handlers = { ++ .fd_can_read = ccid_card_vscard_can_read, ++ .fd_read = ccid_card_vscard_read, ++ .fd_event = ccid_card_vscard_event, ++}; ++ + static int passthru_initfn(CCIDCardState *base) + { + PassthruState *card = DO_UPCAST(PassthruState, base, base); +@@ -281,10 +287,7 @@ static int passthru_initfn(CCIDCardState *base) + card->vscard_in_hdr = 0; + if (card->cs) { + DPRINTF(card, D_INFO, "initing chardev\n"); +- qemu_chr_add_handlers(card->cs, +- ccid_card_vscard_can_read, +- ccid_card_vscard_read, +- ccid_card_vscard_event, card); ++ qemu_chr_add_handlers(card->cs, &passthru_handlers, card); + ccid_card_vscard_send_init(card); + } else { + error_report("missing chardev"); +diff --git a/hw/debugcon.c b/hw/debugcon.c +index 5ee6821..e79a595 100644 +--- a/hw/debugcon.c ++++ b/hw/debugcon.c +@@ -73,7 +73,7 @@ static void debugcon_init_core(DebugconState *s) + exit(1); + } + +- qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); ++ qemu_chr_add_handlers(s->chr, NULL, s); + } + + static int debugcon_isa_initfn(ISADevice *dev) +diff --git a/hw/escc.c b/hw/escc.c +index f6fd919..dfa329a 100644 +--- a/hw/escc.c ++++ b/hw/escc.c +@@ -898,6 +898,12 @@ void slavio_serial_ms_kbd_init(target_phys_addr_t base, qemu_irq irq, + sysbus_mmio_map(s, 0, base); + } + ++static const QemuChrHandlers serial_handlers = { ++ .fd_can_read = serial_can_receive, ++ .fd_read = serial_receive1, ++ .fd_event = serial_event, ++}; ++ + static int escc_init1(SysBusDevice *dev) + { + SerialState *s = FROM_SYSBUS(SerialState, dev); +@@ -911,8 +917,7 @@ static int escc_init1(SysBusDevice *dev) + s->chn[i].chn = 1 - i; + s->chn[i].clock = s->frequency / 2; + if (s->chn[i].chr) { +- qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive, +- serial_receive1, serial_event, &s->chn[i]); ++ qemu_chr_add_handlers(s->chn[i].chr, &serial_handlers, &s->chn[i]); + } + } + s->chn[0].otherchn = &s->chn[1]; +diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c +index b917d4d..ebbad77 100644 +--- a/hw/etraxfs_ser.c ++++ b/hw/etraxfs_ser.c +@@ -202,6 +202,12 @@ static void etraxfs_ser_reset(DeviceState *d) + + } + ++static const QemuChrHandlers serial_handlers = { ++ .fd_can_read = serial_can_receive, ++ .fd_read = serial_receive, ++ .fd_event = serial_event, ++}; ++ + static int etraxfs_ser_init(SysBusDevice *dev) + { + struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); +@@ -212,10 +218,9 @@ static int etraxfs_ser_init(SysBusDevice *dev) + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, ser_regs); + s->chr = qdev_init_chardev(&dev->qdev); +- if (s->chr) +- qemu_chr_add_handlers(s->chr, +- serial_can_receive, serial_receive, +- serial_event, s); ++ if (s->chr) { ++ qemu_chr_add_handlers(s->chr, &serial_handlers, s); ++ } + return 0; + } + +diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c +index 169a56e..310687b 100644 +--- a/hw/grlib_apbuart.c ++++ b/hw/grlib_apbuart.c +@@ -144,16 +144,18 @@ static CPUWriteMemoryFunc * const grlib_apbuart_write[] = { + NULL, NULL, grlib_apbuart_writel, + }; + ++static const QemuChrHandlers grlib_handlers = { ++ .fd_can_read = grlib_apbuart_can_receive, ++ .fd_read = grlib_apbuart_receive, ++ .fd_event = grlib_apbuart_event, ++}; ++ + static int grlib_apbuart_init(SysBusDevice *dev) + { + UART *uart = FROM_SYSBUS(typeof(*uart), dev); + int uart_regs = 0; + +- qemu_chr_add_handlers(uart->chr, +- grlib_apbuart_can_receive, +- grlib_apbuart_receive, +- grlib_apbuart_event, +- uart); ++ qemu_chr_add_handlers(uart->chr, &grlib_handlers, uart); + + sysbus_init_irq(dev, &uart->irq); + +diff --git a/hw/ivshmem.c b/hw/ivshmem.c +index 3055dd2..4f59575 100644 +--- a/hw/ivshmem.c ++++ b/hw/ivshmem.c +@@ -312,6 +312,18 @@ static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { + msix_notify(pdev, entry->vector); + } + ++static const QemuChrHandlers ivshmem_handlers = { ++ .fd_can_read = ivshmem_can_receive, ++ .fd_read = ivshmem_receive, ++ .fd_event = ivshmem_event, ++}; ++ ++static const QemuChrHandlers ivshmem_msi_handlers = { ++ .fd_can_read = ivshmem_can_receive, ++ .fd_read = fake_irqfd, ++ .fd_event = ivshmem_event, ++}; ++ + static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, + int vector) + { +@@ -331,11 +343,10 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, + s->eventfd_table[vector].pdev = &s->dev; + s->eventfd_table[vector].vector = vector; + +- qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, +- ivshmem_event, &s->eventfd_table[vector]); ++ qemu_chr_add_handlers(chr, &ivshmem_msi_handlers, ++ &s->eventfd_table[vector]); + } else { +- qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, +- ivshmem_event, s); ++ qemu_chr_add_handlers(chr, &ivshmem_handlers, s); + } + + return chr; +@@ -666,6 +677,12 @@ static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) + return 0; + } + ++static const QemuChrHandlers ivshmem_server_handlers = { ++ .fd_can_read = ivshmem_can_receive, ++ .fd_read = ivshmem_read, ++ .fd_event = ivshmem_event, ++}; ++ + static int pci_ivshmem_init(PCIDevice *dev) + { + IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); +@@ -749,8 +766,7 @@ static int pci_ivshmem_init(PCIDevice *dev) + + s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *)); + +- qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, +- ivshmem_event, s); ++ qemu_chr_add_handlers(s->server_chr, &ivshmem_server_handlers, s); + } else { + /* just map the file immediately, we're not using a server */ + int fd; +diff --git a/hw/lm32_juart.c b/hw/lm32_juart.c +index fddcf7e..12dd1e8 100644 +--- a/hw/lm32_juart.c ++++ b/hw/lm32_juart.c +@@ -110,13 +110,19 @@ static void juart_reset(DeviceState *d) + s->jrx = 0; + } + ++static const QemuChrHandlers juart_handlers = { ++ .fd_can_read = juart_can_rx, ++ .fd_read = juart_rx, ++ .fd_event = juart_event, ++}; ++ + static int lm32_juart_init(SysBusDevice *dev) + { + LM32JuartState *s = FROM_SYSBUS(typeof(*s), dev); + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { +- qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); ++ qemu_chr_add_handlers(s->chr, juart_handlers, s); + } + + return 0; +diff --git a/hw/lm32_uart.c b/hw/lm32_uart.c +index 09090e9..5438660 100644 +--- a/hw/lm32_uart.c ++++ b/hw/lm32_uart.c +@@ -242,6 +242,12 @@ static void uart_reset(DeviceState *d) + s->regs[R_LSR] = LSR_THRE | LSR_TEMT; + } + ++static const QemuChrHandlers uart_handlers = { ++ .fd_can_read = uart_can_rx, ++ .fd_read = uart_rx, ++ .fd_event = uart_event, ++}; ++ + static int lm32_uart_init(SysBusDevice *dev) + { + LM32UartState *s = FROM_SYSBUS(typeof(*s), dev); +@@ -255,7 +261,7 @@ static int lm32_uart_init(SysBusDevice *dev) + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { +- qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); ++ qemu_chr_add_handlers(s->chr, uart_handlers, s); + } + + return 0; +diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c +index db57096..9928c11 100644 +--- a/hw/mcf_uart.c ++++ b/hw/mcf_uart.c +@@ -268,6 +268,12 @@ static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) + mcf_uart_push_byte(s, buf[0]); + } + ++static const QemuChrHandlers mcf_uart_handlers = { ++ .fd_can_read = mcf_uart_can_receive, ++ .fd_read = mcf_uart_receive, ++ .fd_event = mcf_uart_event, ++}; ++ + void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) + { + mcf_uart_state *s; +@@ -276,8 +282,7 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) + s->chr = chr; + s->irq = irq; + if (chr) { +- qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, +- mcf_uart_event, s); ++ qemu_chr_add_handlers(chr, &mcf_uart_handlers, s); + } + mcf_uart_reset(s); + return s; +diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c +index 56c90da..4412b1b 100644 +--- a/hw/milkymist-uart.c ++++ b/hw/milkymist-uart.c +@@ -133,6 +133,12 @@ static void milkymist_uart_reset(DeviceState *d) + } + } + ++static const QemuChrHandlers uart_handlers = { ++ .fd_can_read = uart_can_rx, ++ .fd_read = uart_rx, ++ .fd_event = uart_event, ++}; ++ + static int milkymist_uart_init(SysBusDevice *dev) + { + MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); +@@ -147,7 +153,7 @@ static int milkymist_uart_init(SysBusDevice *dev) + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { +- qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); ++ qemu_chr_add_handlers(s->chr, uart_handlers, s); + } + + return 0; +diff --git a/hw/pl011.c b/hw/pl011.c +index 3b94b14..7b4f3ae 100644 +--- a/hw/pl011.c ++++ b/hw/pl011.c +@@ -260,6 +260,12 @@ static const VMStateDescription vmstate_pl011 = { + } + }; + ++static const QemuChrHandlers pl011_handlers = { ++ .fd_can_read = pl011_can_receive, ++ .fd_read = pl011_receive, ++ .fd_event = pl011_event, ++}; ++ + static int pl011_init(SysBusDevice *dev, const unsigned char *id) + { + int iomemtype; +@@ -278,8 +284,7 @@ static int pl011_init(SysBusDevice *dev, const unsigned char *id) + s->cr = 0x300; + s->flags = 0x90; + if (s->chr) { +- qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, +- pl011_event, s); ++ qemu_chr_add_handlers(s->chr, &pl011_handlers, s); + } + vmstate_register(&dev->qdev, -1, &vmstate_pl011, s); + return 0; +diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c +index ac5d95d..779081c 100644 +--- a/hw/pxa2xx.c ++++ b/hw/pxa2xx.c +@@ -2019,6 +2019,12 @@ static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id) + return 0; + } + ++static const QemuChrHandlers pxa2xx_handlers = { ++ .fd_can_read = pxa2xx_fir_is_empty, ++ .fd_read = pxa2xx_fir_rx, ++ .fd_event = pxa2xx_fir_event, ++}; ++ + static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, + qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma, + CharDriverState *chr) +@@ -2038,10 +2044,9 @@ static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, + pxa2xx_fir_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x1000, iomemtype); + +- if (chr) +- qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty, +- pxa2xx_fir_rx, pxa2xx_fir_event, s); +- ++ if (chr) { ++ qemu_chr_add_handlers(chr, &pxa2xx_handlers, s); ++ } + register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save, + pxa2xx_fir_load, s); + +diff --git a/hw/serial.c b/hw/serial.c +index 0ee61dd..d496bcf 100644 +--- a/hw/serial.c ++++ b/hw/serial.c +@@ -727,6 +727,12 @@ static void serial_reset(void *opaque) + qemu_irq_lower(s->irq); + } + ++static const QemuChrHandlers serial_handlers = { ++ .fd_can_read = serial_can_receive1, ++ .fd_read = serial_receive1, ++ .fd_event = serial_event, ++}; ++ + static void serial_init_core(SerialState *s) + { + if (!s->chr) { +@@ -741,8 +747,7 @@ static void serial_init_core(SerialState *s) + + qemu_register_reset(serial_reset, s); + +- qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, +- serial_event, s); ++ qemu_chr_add_handlers(s->chr, &serial_handlers, s); + } + + /* Change the main reference oscillator frequency. */ +diff --git a/hw/sh_serial.c b/hw/sh_serial.c +index 191f4a6..8b6460d 100644 +--- a/hw/sh_serial.c ++++ b/hw/sh_serial.c +@@ -350,6 +350,12 @@ static CPUWriteMemoryFunc * const sh_serial_writefn[] = { + &sh_serial_write, + }; + ++static const QemuChrHandlers sh_serial_handlers = { ++ .fd_can_read = sh_serial_can_receive1, ++ .fd_read = sh_serial_receive1, ++ .fd_event = sh_serial_event, ++}; ++ + void sh_serial_init (target_phys_addr_t base, int feat, + uint32_t freq, CharDriverState *chr, + qemu_irq eri_source, +@@ -389,9 +395,9 @@ void sh_serial_init (target_phys_addr_t base, int feat, + + s->chr = chr; + +- if (chr) +- qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, +- sh_serial_event, s); ++ if (chr) { ++ qemu_chr_add_handlers(chr, &sh_serial_handlers, s); ++ } + + s->eri = eri_source; + s->rxi = rxi_source; +diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c +index 6fc0105..5968d85 100644 +--- a/hw/spapr_vty.c ++++ b/hw/spapr_vty.c +@@ -54,12 +54,16 @@ void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) + qemu_chr_write(dev->chardev, buf, len); + } + ++static const QemuChrHandlers vty_handlers = { ++ .fd_can_read = vty_can_receive, ++ .fd_read = vty_receive, ++}; ++ + static int spapr_vty_init(VIOsPAPRDevice *sdev) + { + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + +- qemu_chr_add_handlers(dev->chardev, vty_can_receive, +- vty_receive, NULL, dev); ++ qemu_chr_add_handlers(dev->chardev, vty_handlers, dev); + + return 0; + } +diff --git a/hw/strongarm.c b/hw/strongarm.c +index 0e03d61..c2dce95 100644 +--- a/hw/strongarm.c ++++ b/hw/strongarm.c +@@ -1188,6 +1188,12 @@ static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = { + strongarm_uart_write, + }; + ++static const QemuChrHandlers strongarm_uart_handlers = { ++ .fd_can_read = strongarm_uart_can_receive, ++ .fd_read = strongarm_uart_receive, ++ .fd_event = strongarm_uart_event, ++}; ++ + static int strongarm_uart_init(SysBusDevice *dev) + { + StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev); +@@ -1202,11 +1208,7 @@ static int strongarm_uart_init(SysBusDevice *dev) + s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s); + + if (s->chr) { +- qemu_chr_add_handlers(s->chr, +- strongarm_uart_can_receive, +- strongarm_uart_receive, +- strongarm_uart_event, +- s); ++ qemu_chr_add_handlers(s->chr, &strongarm_uart_handlers, s); + } + + return 0; +diff --git a/hw/syborg_serial.c b/hw/syborg_serial.c +index 2ef7175..ac68022 100644 +--- a/hw/syborg_serial.c ++++ b/hw/syborg_serial.c +@@ -292,6 +292,12 @@ static const VMStateDescription vmstate_syborg_serial = { + } + }; + ++static const QemuChrHandlers syborg_serial_handlers = { ++ .fd_can_read = syborg_serial_can_receive, ++ .fd_read = syborg_serial_receive, ++ .fd_event = syborg_serial_event, ++}; ++ + static int syborg_serial_init(SysBusDevice *dev) + { + SyborgSerialState *s = FROM_SYSBUS(SyborgSerialState, dev); +@@ -304,8 +310,7 @@ static int syborg_serial_init(SysBusDevice *dev) + sysbus_init_mmio(dev, 0x1000, iomemtype); + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { +- qemu_chr_add_handlers(s->chr, syborg_serial_can_receive, +- syborg_serial_receive, syborg_serial_event, s); ++ qemu_chr_add_handlers(s->chr, &syborg_serial_handlers, s); + } + if (s->fifo_size <= 0) { + fprintf(stderr, "syborg_serial: fifo too small\n"); +diff --git a/hw/usb-serial.c b/hw/usb-serial.c +index 59cb0fb..bebd900 100644 +--- a/hw/usb-serial.c ++++ b/hw/usb-serial.c +@@ -476,6 +476,12 @@ static void usb_serial_event(void *opaque, int event) + } + } + ++static const QemuChrHandlers usb_serial_handlers = { ++ .fd_can_read = usb_serial_can_read, ++ .fd_read = usb_serial_read, ++ .fd_event = usb_serial_event, ++}; ++ + static int usb_serial_initfn(USBDevice *dev) + { + USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); +@@ -487,8 +493,7 @@ static int usb_serial_initfn(USBDevice *dev) + return -1; + } + +- qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, +- usb_serial_event, s); ++ qemu_chr_add_handlers(s->cs, &usb_serial_handlers, s); + usb_serial_handle_reset(dev); + return 0; + } +diff --git a/hw/virtio-console.c b/hw/virtio-console.c +index b076331..147a467 100644 +--- a/hw/virtio-console.c ++++ b/hw/virtio-console.c +@@ -74,6 +74,12 @@ static void chr_event(void *opaque, int event) + } + } + ++static const QemuChrHandlers chr_handlers = { ++ .fd_can_read = chr_can_read, ++ .fd_read = chr_read, ++ .fd_event = chr_event, ++}; ++ + static int virtconsole_initfn(VirtIOSerialPort *port) + { + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); +@@ -86,8 +92,7 @@ static int virtconsole_initfn(VirtIOSerialPort *port) + } + + if (vcon->chr) { +- qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, +- vcon); ++ qemu_chr_add_handlers(vcon->chr, &chr_handlers, vcon); + info->have_data = flush_buf; + info->guest_open = guest_open; + info->guest_close = guest_close; +@@ -105,7 +110,7 @@ static int virtconsole_exitfn(VirtIOSerialPort *port) + * Instead of closing the chardev, free it so it can be used + * for other purposes. + */ +- qemu_chr_add_handlers(vcon->chr, NULL, NULL, NULL, NULL); ++ qemu_chr_add_handlers(vcon->chr, NULL, NULL); + } + + return 0; +diff --git a/hw/xen_console.c b/hw/xen_console.c +index c6c8163..4c39310 100644 +--- a/hw/xen_console.c ++++ b/hw/xen_console.c +@@ -201,6 +201,11 @@ static int con_init(struct XenDevice *xendev) + return 0; + } + ++static const QemuChrHandlers xencons_handlers = { ++ .fd_can_read = xencons_can_receive, ++ .fd_read = xencons_receive, ++}; ++ + static int con_connect(struct XenDevice *xendev) + { + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); +@@ -221,9 +226,9 @@ static int con_connect(struct XenDevice *xendev) + return -1; + + xen_be_bind_evtchn(&con->xendev); +- if (con->chr) +- qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, +- NULL, con); ++ if (con->chr) { ++ qemu_chr_add_handlers(con->chr, &xencons_handlers, con); ++ } + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, +@@ -237,8 +242,9 @@ static void con_disconnect(struct XenDevice *xendev) + { + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + +- if (con->chr) +- qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); ++ if (con->chr) { ++ qemu_chr_add_handlers(con->chr, NULL, NULL); ++ } + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { +diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c +index 9b94e98..1845577 100644 +--- a/hw/xilinx_uartlite.c ++++ b/hw/xilinx_uartlite.c +@@ -193,6 +193,12 @@ static void uart_event(void *opaque, int event) + + } + ++static const QemuChrHandlers uart_handlers = { ++ .fd_can_read = uart_can_rx, ++ .fd_read = uart_rx, ++ .fd_event = uart_event, ++}; ++ + static int xilinx_uartlite_init(SysBusDevice *dev) + { + struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev); +@@ -206,8 +212,9 @@ static int xilinx_uartlite_init(SysBusDevice *dev) + sysbus_init_mmio(dev, R_MAX * 4, uart_regs); + + s->chr = qdev_init_chardev(&dev->qdev); +- if (s->chr) +- qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); ++ if (s->chr) { ++ qemu_chr_add_handlers(s->chr, &uart_handlers, s); ++ } + return 0; + } + +diff --git a/monitor.c b/monitor.c +index a6388a9..5e7ce62 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -5238,6 +5238,18 @@ static void monitor_event(void *opaque, int event) + * End: + */ + ++static const QemuChrHandlers monitor_handlers = { ++ .fd_can_read = monitor_can_read, ++ .fd_read = monitor_read, ++ .fd_event = monitor_event, ++}; ++ ++static const QemuChrHandlers monitor_control_handlers = { ++ .fd_can_read = monitor_can_read, ++ .fd_read = monitor_control_read, ++ .fd_event = monitor_control_event, ++}; ++ + void monitor_init(CharDriverState *chr, int flags) + { + static int is_first_init = 1; +@@ -5260,12 +5272,10 @@ void monitor_init(CharDriverState *chr, int flags) + if (monitor_ctrl_mode(mon)) { + mon->mc = qemu_mallocz(sizeof(MonitorControl)); + /* Control mode requires special handlers */ +- qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read, +- monitor_control_event, mon); ++ qemu_chr_add_handlers(chr, &monitor_control_handlers, mon); + qemu_chr_set_echo(chr, true); + } else { +- qemu_chr_add_handlers(chr, monitor_can_read, monitor_read, +- monitor_event, mon); ++ qemu_chr_add_handlers(chr, &monitor_handlers, mon); + } + + QLIST_INSERT_HEAD(&mon_list, mon, entry); +diff --git a/net/slirp.c b/net/slirp.c +index e057a14..6eb0c51 100644 +--- a/net/slirp.c ++++ b/net/slirp.c +@@ -576,6 +576,11 @@ static void guestfwd_read(void *opaque, const uint8_t *buf, int size) + slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size); + } + ++static const QemuChrHandlers guestfwd_handlers = { ++ .fd_can_read = guestfwd_can_read, ++ .fd_read = guestfwd_read, ++}; ++ + static int slirp_guestfwd(SlirpState *s, const char *config_str, + int legacy_format) + { +@@ -632,8 +637,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, + fwd->port = port; + fwd->slirp = s->slirp; + +- qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read, +- NULL, fwd); ++ qemu_chr_add_handlers(fwd->hd, &guestfwd_handlers, fwd); + return 0; + + fail_syntax: +diff --git a/qemu-char.c b/qemu-char.c +index 75efa60..ee763d5 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -190,19 +190,26 @@ void qemu_chr_send_event(CharDriverState *s, int event) + s->chr_send_event(s, event); + } + ++static const QemuChrHandlers null_handlers = { ++ /* All handlers are initialised to NULL */ ++}; ++ + void qemu_chr_add_handlers(CharDriverState *s, +- IOCanReadHandler *fd_can_read, +- IOReadHandler *fd_read, +- IOEventHandler *fd_event, +- void *opaque) ++ const QemuChrHandlers *handlers, void *opaque) + { +- if (!opaque && !fd_can_read && !fd_read && !fd_event) { ++ if (!s) { ++ return; ++ } ++ if (!opaque && !handlers) { + /* chr driver being released. */ + ++s->avail_connections; + } +- s->chr_can_read = fd_can_read; +- s->chr_read = fd_read; +- s->chr_event = fd_event; ++ if (!handlers) { ++ handlers = &null_handlers; ++ } ++ s->chr_can_read = handlers->fd_can_read; ++ s->chr_read = handlers->fd_read; ++ s->chr_event = handlers->fd_event; + s->handler_opaque = opaque; + if (s->chr_update_read_handler) + s->chr_update_read_handler(s); +@@ -440,6 +447,12 @@ static void mux_chr_event(void *opaque, int event) + mux_chr_send_event(d, i, event); + } + ++static const QemuChrHandlers mux_chr_handlers = { ++ .fd_can_read = mux_chr_can_read, ++ .fd_read = mux_chr_read, ++ .fd_event = mux_chr_event, ++}; ++ + static void mux_chr_update_read_handler(CharDriverState *chr) + { + MuxDriver *d = chr->opaque; +@@ -454,8 +467,7 @@ static void mux_chr_update_read_handler(CharDriverState *chr) + d->chr_event[d->mux_cnt] = chr->chr_event; + /* Fix up the real driver with mux routines */ + if (d->mux_cnt == 0) { +- qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, +- mux_chr_event, chr); ++ qemu_chr_add_handlers(d->drv, &mux_chr_handlers, chr); + } + if (d->focus != -1) { + mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); +diff --git a/qemu-char.h b/qemu-char.h +index 892c6da..c8e3f2b 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -1,6 +1,7 @@ + #ifndef QEMU_CHAR_H + #define QEMU_CHAR_H + ++#include + #include "qemu-common.h" + #include "qemu-queue.h" + #include "qemu-option.h" +@@ -76,6 +77,13 @@ struct CharDriverState { + QTAILQ_ENTRY(CharDriverState) next; + }; + ++typedef struct QemuChrHandlers { ++ IOCanReadHandler *fd_can_read; ++ IOReadHandler *fd_read; ++ IOHandler *fd_write_unblocked; ++ IOEventHandler *fd_event; ++} QemuChrHandlers; ++ + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); + CharDriverState *qemu_chr_open_opts(QemuOpts *opts, + void (*init)(struct CharDriverState *s)); +@@ -88,10 +96,7 @@ void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len); + void qemu_chr_send_event(CharDriverState *s, int event); +-void qemu_chr_add_handlers(CharDriverState *s, +- IOCanReadHandler *fd_can_read, +- IOReadHandler *fd_read, +- IOEventHandler *fd_event, ++void qemu_chr_add_handlers(CharDriverState *s, const QemuChrHandlers *handlers, + void *opaque); + int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg); + void qemu_chr_generic_open(CharDriverState *s); +diff --git a/usb-redir.c b/usb-redir.c +index e212993..e2b5a2c 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -782,6 +782,12 @@ static void usbredir_chardev_event(void *opaque, int event) + } + } + ++static const QemuChrHandlers usbredir_chr_handlers = { ++ .fd_can_read = usbredir_chardev_can_read, ++ .fd_read = usbredir_chardev_read, ++ .fd_event = usbredir_chardev_event, ++}; ++ + /* + * init + destroy + */ +@@ -807,8 +813,7 @@ static int usbredir_initfn(USBDevice *udev) + /* We'll do the attach once we receive the speed from the usb-host */ + udev->auto_attach = 0; + +- qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, +- usbredir_chardev_read, usbredir_chardev_event, dev); ++ qemu_chr_add_handlers(dev->cs, &usbredir_chr_handlers, dev); + + return 0; + } +-- +1.7.5.1 + diff --git a/0027-iohandlers-Add-enable-disable_write_fd_handler-funct.patch b/0027-iohandlers-Add-enable-disable_write_fd_handler-funct.patch new file mode 100644 index 0000000..9c6620a --- /dev/null +++ b/0027-iohandlers-Add-enable-disable_write_fd_handler-funct.patch @@ -0,0 +1,77 @@ +From 328ddb228ed9e5509b8418b1f34de6d6aff83d62 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 20:32:58 +0100 +Subject: [PATCH 27/35] iohandlers: Add enable/disable_write_fd_handler() + functions + +These will be used to provide a cleaner API for the nonblocking case. + +Signed-off-by: Amit Shah +--- + iohandler.c | 35 +++++++++++++++++++++++++++++++++++ + qemu-char.h | 3 +++ + 2 files changed, 38 insertions(+), 0 deletions(-) + +diff --git a/iohandler.c b/iohandler.c +index 2b82421..8e6628b 100644 +--- a/iohandler.c ++++ b/iohandler.c +@@ -44,6 +44,41 @@ typedef struct IOHandlerRecord { + static QLIST_HEAD(, IOHandlerRecord) io_handlers = + QLIST_HEAD_INITIALIZER(io_handlers); + ++static IOHandlerRecord *find_iohandler(int fd) ++{ ++ IOHandlerRecord *ioh; ++ ++ QLIST_FOREACH(ioh, &io_handlers, next) { ++ if (ioh->fd == fd) { ++ return ioh; ++ } ++ } ++ return NULL; ++} ++ ++void enable_write_fd_handler(int fd, IOHandler *fd_write) ++{ ++ IOHandlerRecord *ioh; ++ ++ ioh = find_iohandler(fd); ++ if (!ioh) { ++ return; ++ } ++ ++ ioh->fd_write = fd_write; ++} ++ ++void disable_write_fd_handler(int fd) ++{ ++ IOHandlerRecord *ioh; ++ ++ ioh = find_iohandler(fd); ++ if (!ioh) { ++ return; ++ } ++ ++ ioh->fd_write = NULL; ++} + + /* XXX: fd_read_poll should be suppressed, but an API change is + necessary in the character devices to suppress fd_can_read(). */ +diff --git a/qemu-char.h b/qemu-char.h +index c8e3f2b..7d5dc6c 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -121,6 +121,9 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr); + + /* async I/O support */ + ++void enable_write_fd_handler(int fd, IOHandler *fd_write); ++void disable_write_fd_handler(int fd); ++ + int qemu_set_fd_handler2(int fd, + IOCanReadHandler *fd_read_poll, + IOHandler *fd_read, +-- +1.7.5.1 + diff --git a/0028-char-Add-framework-for-a-write-unblocked-callback.patch b/0028-char-Add-framework-for-a-write-unblocked-callback.patch new file mode 100644 index 0000000..dc32f9b --- /dev/null +++ b/0028-char-Add-framework-for-a-write-unblocked-callback.patch @@ -0,0 +1,61 @@ +From 59ff8a6457e82aadad7529602da68406de374a33 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 21:41:42 +0100 +Subject: [PATCH 28/35] char: Add framework for a 'write unblocked' callback + +The char layer can let users know that the driver will block on further +input. For users interested in not blocking, they can assign a function +pointer that will be called back when the driver becomes writable. This +patch just adds the function pointers to the CharDriverState structure, +future patches will enable the nonblocking and callback functionality. + +Signed-off-by: Amit Shah +--- + qemu-char.c | 3 +++ + qemu-char.h | 4 ++++ + 2 files changed, 7 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index ee763d5..830f0c3 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -209,11 +209,14 @@ void qemu_chr_add_handlers(CharDriverState *s, + } + s->chr_can_read = handlers->fd_can_read; + s->chr_read = handlers->fd_read; ++ s->chr_write_unblocked = handlers->fd_write_unblocked; + s->chr_event = handlers->fd_event; + s->handler_opaque = opaque; + if (s->chr_update_read_handler) + s->chr_update_read_handler(s); + ++ s->write_blocked = false; ++ + /* We're connecting to an already opened device, so let's make sure we + also get the open event */ + if (s->opened) { +diff --git a/qemu-char.h b/qemu-char.h +index 7d5dc6c..e942bdf 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -61,6 +61,9 @@ struct CharDriverState { + IOEventHandler *chr_event; + IOCanReadHandler *chr_can_read; + IOReadHandler *chr_read; ++ IOHandler *chr_write_unblocked; ++ void (*chr_enable_write_fd_handler)(struct CharDriverState *chr); ++ void (*chr_disable_write_fd_handler)(struct CharDriverState *chr); + void *handler_opaque; + void (*chr_send_event)(struct CharDriverState *chr, int event); + void (*chr_close)(struct CharDriverState *chr); +@@ -74,6 +77,7 @@ struct CharDriverState { + char *filename; + int opened; + int avail_connections; ++ bool write_blocked; /* Are we in a blocked state? */ + QTAILQ_ENTRY(CharDriverState) next; + }; + +-- +1.7.5.1 + diff --git a/0029-char-Update-send_all-to-handle-nonblocking-chardev-w.patch b/0029-char-Update-send_all-to-handle-nonblocking-chardev-w.patch new file mode 100644 index 0000000..db4edba --- /dev/null +++ b/0029-char-Update-send_all-to-handle-nonblocking-chardev-w.patch @@ -0,0 +1,199 @@ +From 4178a8240badad028ffd13482352510ad6a050e4 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:00:27 +0100 +Subject: [PATCH 29/35] char: Update send_all() to handle nonblocking chardev + write requests + +The send_all function is modified to return to the caller in case the +driver cannot handle any more data. It returns -EAGAIN or +WSAEWOULDBLOCK on non-Windows and Windows platforms respectively. This +is only done when the caller sets a callback function handler indicating +it's not interested in blocking till the driver has written out all the +data. + +Currently there's no driver or caller that supports this. Future +commits will add such capability. + +Signed-off-by: Amit Shah +--- + net/socket.c | 4 +- + qemu-char.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- + qemu_socket.h | 2 +- + 3 files changed, 77 insertions(+), 9 deletions(-) + +diff --git a/net/socket.c b/net/socket.c +index bc1bf58..a40687a 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -56,8 +56,8 @@ static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_ + uint32_t len; + len = htonl(size); + +- send_all(s->fd, (const uint8_t *)&len, sizeof(len)); +- return send_all(s->fd, buf, size); ++ send_all(NULL, s->fd, (const uint8_t *)&len, sizeof(len)); ++ return send_all(NULL, s->fd, buf, size); + } + + static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size) +diff --git a/qemu-char.c b/qemu-char.c +index 830f0c3..88b22c3 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -506,7 +506,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) + + + #ifdef _WIN32 +-int send_all(int fd, const void *buf, int len1) ++static int do_send(int fd, const void *buf, int len1, bool nonblock) + { + int ret, len; + +@@ -514,9 +514,14 @@ int send_all(int fd, const void *buf, int len1) + while (len > 0) { + ret = send(fd, buf, len, 0); + if (ret < 0) { ++ if (nonblock && len1 - len) { ++ return len1 - len; ++ } + errno = WSAGetLastError(); + if (errno != WSAEWOULDBLOCK) { + return -1; ++ } else if (errno == WSAEWOULDBLOCK && nonblock) { ++ return WSAEWOULDBLOCK; + } + } else if (ret == 0) { + break; +@@ -530,7 +535,7 @@ int send_all(int fd, const void *buf, int len1) + + #else + +-int send_all(int fd, const void *_buf, int len1) ++static int do_send(int fd, const void *_buf, int len1, bool nonblock) + { + int ret, len; + const uint8_t *buf = _buf; +@@ -539,8 +544,15 @@ int send_all(int fd, const void *_buf, int len1) + while (len > 0) { + ret = write(fd, buf, len); + if (ret < 0) { +- if (errno != EINTR && errno != EAGAIN) ++ if (nonblock && len1 - len) { ++ return len1 - len; ++ } ++ if (errno == EAGAIN && nonblock) { ++ return -EAGAIN; ++ } ++ if (errno != EINTR && errno != EAGAIN) { + return -1; ++ } + } else if (ret == 0) { + break; + } else { +@@ -552,6 +564,55 @@ int send_all(int fd, const void *_buf, int len1) + } + #endif /* !_WIN32 */ + ++int send_all(CharDriverState *chr, int fd, const void *_buf, int len1) ++{ ++ int ret, eagain_errno; ++ bool nonblock; ++ ++ if (chr && chr->write_blocked) { ++ /* ++ * We don't handle this situation: the caller should not send ++ * us data while we're blocked. ++ * ++ * We could buffer this data here but that'll only encourage ++ * bad behaviour on part of the callers. ++ * ++ * Also, the data already in fd's buffers isn't easily ++ * migratable. If we want full migration support, all the ++ * data landing here needs to be buffered and on migration, ++ * anything that's unsent needs to be transferred to the ++ * dest. machine (which again isn't a very good way of solving ++ * the problem, as the src may become writable just during ++ * migration and the reader could receive some data twice, ++ * essentially corrupting the data). ++ */ ++ abort(); ++ } ++ ++ nonblock = false; ++ /* ++ * Ensure the char backend is able to receive and handle the ++ * 'write unblocked' event before we turn on nonblock support. ++ */ ++ if (chr && chr->chr_enable_write_fd_handler && chr->chr_write_unblocked) { ++ nonblock = true; ++ } ++ ret = do_send(fd, _buf, len1, nonblock); ++ ++#ifdef _WIN32 ++ eagain_errno = WSAEWOULDBLOCK; ++#else ++ eagain_errno = -EAGAIN; ++#endif ++ ++ if (nonblock && (ret == eagain_errno || (ret >= 0 && ret < len1))) { ++ /* Update fd handler to wake up when chr becomes writable */ ++ chr->chr_enable_write_fd_handler(chr); ++ chr->write_blocked = true; ++ } ++ return ret; ++} ++ + #ifndef _WIN32 + + typedef struct { +@@ -565,7 +626,7 @@ static int stdio_nb_clients = 0; + static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + FDCharDriver *s = chr->opaque; +- return send_all(s->fd_out, buf, len); ++ return send_all(chr, s->fd_out, buf, len); + } + + static int fd_chr_read_poll(void *opaque) +@@ -881,7 +942,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + pty_chr_update_read_handler(chr); + return 0; + } +- return send_all(s->fd, buf, len); ++ return send_all(chr, s->fd, buf, len); + } + + static int pty_chr_read_poll(void *opaque) +@@ -1950,8 +2011,15 @@ static void tcp_closed(void *opaque) + static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + TCPCharDriver *s = chr->opaque; ++ + if (s->connected) { +- return send_all(s->fd, buf, len); ++ int ret; ++ ++ ret = send_all(chr, s->fd, buf, len); ++ if (ret == -1 && errno == EPIPE) { ++ tcp_closed(chr); ++ } ++ return ret; + } else { + /* XXX: indicate an error ? */ + return len; +diff --git a/qemu_socket.h b/qemu_socket.h +index 180e4db..6f453e5 100644 +--- a/qemu_socket.h ++++ b/qemu_socket.h +@@ -36,7 +36,7 @@ int inet_aton(const char *cp, struct in_addr *ia); + int qemu_socket(int domain, int type, int protocol); + int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); + void socket_set_nonblock(int fd); +-int send_all(int fd, const void *buf, int len1); ++int send_all(CharDriverState *chr, int fd, const void *buf, int len1); + + /* New, ipv6-ready socket helper functions, see qemu-sockets.c */ + int inet_listen_opts(QemuOpts *opts, int port_offset); +-- +1.7.5.1 + diff --git a/0030-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch b/0030-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch new file mode 100644 index 0000000..0594e32 --- /dev/null +++ b/0030-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch @@ -0,0 +1,81 @@ +From 56cbfb533c04cde3a55c1345ea0f9097b1ab13fa Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:02:47 +0100 +Subject: [PATCH 30/35] char: Equip the unix/tcp backend to handle nonblocking + writes# + +Now that the infrastructure is in place to return -EAGAIN to callers, +individual char drivers can set their update_fd_handlers() function to +set or remove an fd's write handler. This handler checks if the driver +became writable. + +A generic callback routine is used for unblocking writes and letting +users of chardevs know that a driver became writable again. + +Signed-off-by: Amit Shah +--- + qemu-char.c | 34 ++++++++++++++++++++++++++++++++++ + 1 files changed, 34 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index 88b22c3..e9d7f0a 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -106,6 +106,19 @@ + static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = + QTAILQ_HEAD_INITIALIZER(chardevs); + ++/* ++ * Generic routine that gets called when chardev becomes writable. ++ * Lets chardev user know it's OK to send more data. ++ */ ++static void char_write_unblocked(void *opaque) ++{ ++ CharDriverState *chr = opaque; ++ ++ chr->write_blocked = false; ++ chr->chr_disable_write_fd_handler(chr); ++ chr->chr_write_unblocked(chr->handler_opaque); ++} ++ + static void qemu_chr_event(CharDriverState *s, int event) + { + /* Keep track if the char device is open */ +@@ -2268,6 +2281,25 @@ static void tcp_chr_close(CharDriverState *chr) + qemu_chr_event(chr, CHR_EVENT_CLOSED); + } + ++static void tcp_enable_write_fd_handler(CharDriverState *chr) ++{ ++ TCPCharDriver *s = chr->opaque; ++ ++ /* ++ * This function is called only after tcp_chr_connect() is called ++ * (either in 'server' mode or client mode. So we're sure of ++ * s->fd being initialised. ++ */ ++ enable_write_fd_handler(s->fd, char_write_unblocked); ++} ++ ++static void tcp_disable_write_fd_handler(CharDriverState *chr) ++{ ++ TCPCharDriver *s = chr->opaque; ++ ++ disable_write_fd_handler(s->fd); ++} ++ + static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) + { + CharDriverState *chr = NULL; +@@ -2320,6 +2352,8 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) + chr->chr_write = tcp_chr_write; + chr->chr_close = tcp_chr_close; + chr->get_msgfd = tcp_get_msgfd; ++ chr->chr_enable_write_fd_handler = tcp_enable_write_fd_handler; ++ chr->chr_disable_write_fd_handler = tcp_disable_write_fd_handler; + + if (is_listen) { + s->listen_fd = fd; +-- +1.7.5.1 + diff --git a/0031-char-Throttle-when-host-connection-is-down.patch b/0031-char-Throttle-when-host-connection-is-down.patch new file mode 100644 index 0000000..a60c0e7 --- /dev/null +++ b/0031-char-Throttle-when-host-connection-is-down.patch @@ -0,0 +1,56 @@ +From 00cf9482be18cdacda0ae9b207b84a7e86ca1d11 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:05:10 +0100 +Subject: [PATCH 31/35] char: Throttle when host connection is down# + +When the host-side connection goes down, throttle the virtio-serial bus +and later unthrottle when a connection gets established. This helps +prevent any lost IO (guest->host) while the host connection was down. + +Bugzilla: 621484 + +This commit actually helps the bug mentioned above as no writes will now +get lost because of the throttling done here. With just the patches +sent earlier for that bug, one write will end up getting lost in the +worst case (host d/c, guest write, host connect). + +Signed-off-by: Amit Shah +--- + qemu-char.c | 14 ++++++++++++++ + 1 files changed, 14 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index e9d7f0a..77ab1ed 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -140,6 +140,9 @@ static void qemu_chr_generic_open_bh(void *opaque) + { + CharDriverState *s = opaque; + qemu_chr_event(s, CHR_EVENT_OPENED); ++ if (s->write_blocked) { ++ char_write_unblocked(s); ++ } + qemu_bh_delete(s->bh); + s->bh = NULL; + } +@@ -2031,6 +2034,17 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + ret = send_all(chr, s->fd, buf, len); + if (ret == -1 && errno == EPIPE) { + tcp_closed(chr); ++ ++ if (chr->chr_enable_write_fd_handler && chr->chr_write_unblocked) { ++ /* ++ * Since we haven't written out anything, let's say ++ * we're throttled. This will prevent any output from ++ * the guest getting lost if host-side chardev goes ++ * down. Unthrottle when we re-connect. ++ */ ++ chr->write_blocked = true; ++ return 0; ++ } + } + return ret; + } else { +-- +1.7.5.1 + diff --git a/0032-virtio-console-Enable-port-throttling-when-chardev-i.patch b/0032-virtio-console-Enable-port-throttling-when-chardev-i.patch new file mode 100644 index 0000000..4d130a8 --- /dev/null +++ b/0032-virtio-console-Enable-port-throttling-when-chardev-i.patch @@ -0,0 +1,49 @@ +From ba203585e61d1fbe6ba45683d958d261678641ad Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:06:41 +0100 +Subject: [PATCH 32/35] virtio-console: Enable port throttling when chardev is + slow to consume data + +When a chardev indicates it can't accept more data, we tell the +virtio-serial code to stop sending us any more data till we tell +otherwise. This helps in guests continuing to run normally while the vq +keeps getting full and eventually the guest stops queueing more data. +As soon as the chardev indicates it can accept more data, start pushing! + +Signed-off-by: Amit Shah +--- + hw/virtio-console.c | 11 +++++++++++ + 1 files changed, 11 insertions(+), 0 deletions(-) + +diff --git a/hw/virtio-console.c b/hw/virtio-console.c +index 147a467..9286f6e 100644 +--- a/hw/virtio-console.c ++++ b/hw/virtio-console.c +@@ -19,6 +19,16 @@ typedef struct VirtConsole { + CharDriverState *chr; + } VirtConsole; + ++/* ++ * Callback function that's called from chardevs when backend becomes ++ * writable. ++ */ ++static void chr_write_unblocked(void *opaque) ++{ ++ VirtConsole *vcon = opaque; ++ ++ virtio_serial_throttle_port(&vcon->port, false); ++} + + /* Callback function that's called when the guest sends us data */ + static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) +@@ -78,6 +88,7 @@ static const QemuChrHandlers chr_handlers = { + .fd_can_read = chr_can_read, + .fd_read = chr_read, + .fd_event = chr_event, ++ .fd_write_unblocked = chr_write_unblocked, + }; + + static int virtconsole_initfn(VirtIOSerialPort *port) +-- +1.7.5.1 + diff --git a/0033-spice-qemu-char.c-add-throttling.patch b/0033-spice-qemu-char.c-add-throttling.patch new file mode 100644 index 0000000..0696f81 --- /dev/null +++ b/0033-spice-qemu-char.c-add-throttling.patch @@ -0,0 +1,133 @@ +From 7322ad318ecde8669e68ef1314e97b4553a327fa Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Tue, 22 Mar 2011 12:27:59 +0200 +Subject: [PATCH 33/35] spice-qemu-char.c: add throttling + +BZ: 672191 + +upstream: not submitted (explained below) + +Adds throttling support to spicevmc chardev. Uses a timer to avoid recursing: +1. spice-server: reds.c: read_from_vdi_port +2. qemu: spice-qemu-char.c: vmc_read +3. chr_write_unblocked + (calls virtio_serial_throttle_port(port, false)) +4. qemu: virtio ... +5. qemu: spice-qemu-char.c: spice_chr_write +6. qemu: spice-qemu-char.c: wakeup (calls into spice-server) +7. spice-server: ... +8. qemu: spice-qemu-char.c: vmc_read + +Instead, in vmc_read if we were throttled and we are just about to return +all the bytes we will set a timer to be triggered immediately to call +chr_write_unblocked. Then we return after 2 above, and 3 is called from the +timer callback. This also means we can later remove some ugly recursion protection +from spice-server. + +The other tricky point in this patch is not returning the leftover chunk twice. +When we throttle, by definition we have data that spice server didn't consume. +It is being kept by virtio-serial, and by us. The next vmc_read callback needs +to not return it, but just do unthrottling. Then virtio will give us the remaining +chunk as usual in spice_chr_write, and we will pass it to spice server in the +next vmc_read. + +This patch relies on Amit's series to expose throttling to chardev's, which +was not accepted upstream, and will not be accepted upstream until the mainloop +is reworked to use glib. +--- + spice-qemu-char.c | 39 +++++++++++++++++++++++++++++++++++---- + 1 files changed, 35 insertions(+), 4 deletions(-) + +diff --git a/spice-qemu-char.c b/spice-qemu-char.c +index 605c241..9348f65 100644 +--- a/spice-qemu-char.c ++++ b/spice-qemu-char.c +@@ -1,4 +1,6 @@ + #include "config-host.h" ++#include "qemu-common.h" ++#include "qemu-timer.h" + #include "trace.h" + #include "ui/qemu-spice.h" + #include +@@ -25,6 +27,7 @@ typedef struct SpiceCharDriver { + uint8_t *datapos; + ssize_t bufsize, datalen; + uint32_t debug; ++ QEMUTimer *unblock_timer; + } SpiceCharDriver; + + static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) +@@ -50,6 +53,17 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) + return out; + } + ++static void spice_chr_unblock(void *opaque) ++{ ++ SpiceCharDriver *scd = opaque; ++ ++ if (scd->chr->chr_write_unblocked == NULL) { ++ dprintf(scd, 1, "%s: backend doesn't support unthrottling.\n", __func__); ++ return; ++ } ++ scd->chr->chr_write_unblocked(scd->chr->handler_opaque); ++} ++ + static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) + { + SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); +@@ -61,9 +75,16 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) + scd->datapos += bytes; + scd->datalen -= bytes; + assert(scd->datalen >= 0); +- if (scd->datalen == 0) { +- scd->datapos = 0; +- } ++ } ++ if (scd->datalen == 0 && scd->chr->write_blocked) { ++ dprintf(scd, 1, "%s: unthrottling (%d)\n", __func__, bytes); ++ scd->chr->write_blocked = false; ++ /* ++ * set a timer instead of calling scd->chr->chr_write_unblocked directly, ++ * because that will call back into spice_chr_write (see ++ * virtio-console.c:chr_write_unblocked), which is unwanted. ++ */ ++ qemu_mod_timer(scd->unblock_timer, 0); + } + trace_spice_vmc_read(bytes, len); + return bytes; +@@ -106,6 +127,7 @@ static void vmc_unregister_interface(SpiceCharDriver *scd) + static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + SpiceCharDriver *s = chr->opaque; ++ int read_bytes; + + dprintf(s, 2, "%s: %d\n", __func__, len); + vmc_register_interface(s); +@@ -118,7 +140,15 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + s->datapos = s->buffer; + s->datalen = len; + spice_server_char_device_wakeup(&s->sin); +- return len; ++ read_bytes = len - s->datalen; ++ if (read_bytes != len) { ++ dprintf(s, 1, "%s: throttling: %d < %d (%zd)\n", __func__, ++ read_bytes, len, s->bufsize); ++ s->chr->write_blocked = true; ++ /* We'll get passed in the unconsumed data with the next call */ ++ s->datalen = 0; ++ } ++ return read_bytes; + } + + static void spice_chr_close(struct CharDriverState *chr) +@@ -196,6 +226,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) + chr->chr_close = spice_chr_close; + chr->chr_guest_open = spice_chr_guest_open; + chr->chr_guest_close = spice_chr_guest_close; ++ s->unblock_timer = qemu_new_timer_ms(vm_clock, spice_chr_unblock, s); + + qemu_chr_generic_open(chr); + +-- +1.7.5.1 + diff --git a/0034-spice-qemu-char.c-remove-intermediate-buffer.patch b/0034-spice-qemu-char.c-remove-intermediate-buffer.patch new file mode 100644 index 0000000..c2a5c69 --- /dev/null +++ b/0034-spice-qemu-char.c-remove-intermediate-buffer.patch @@ -0,0 +1,71 @@ +From 8b7c5738faa2c7851ecc92182467a564bf7c9109 Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Tue, 22 Mar 2011 12:28:00 +0200 +Subject: [PATCH 34/35] spice-qemu-char.c: remove intermediate buffer + +BZ: 672191 +upstream: not submitted (explained below) + +virtio-serial's buffer is valid when it calls us, and we don't +access it otherwise: vmc_read is only called in response to wakeup, +or else we set datalen=0 and throttle. Then vmc_read is called back, +we return 0 (not accessing the buffer) and set the timer to unthrottle. + +Also make datalen int and not ssize_t (to fit spice_chr_write signature). + +This relied on the previous patch that introduces throttling, which +can't go upstream right now as explained in that patch. +--- + spice-qemu-char.c | 18 ++++++------------ + 1 files changed, 6 insertions(+), 12 deletions(-) + +diff --git a/spice-qemu-char.c b/spice-qemu-char.c +index 9348f65..ce75e91 100644 +--- a/spice-qemu-char.c ++++ b/spice-qemu-char.c +@@ -23,9 +23,8 @@ typedef struct SpiceCharDriver { + SpiceCharDeviceInstance sin; + char *subtype; + bool active; +- uint8_t *buffer; +- uint8_t *datapos; +- ssize_t bufsize, datalen; ++ const uint8_t *datapos; ++ int datalen; + uint32_t debug; + QEMUTimer *unblock_timer; + } SpiceCharDriver; +@@ -69,7 +68,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) + SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + int bytes = MIN(len, scd->datalen); + +- dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen); ++ dprintf(scd, 2, "%s: %p %d/%d/%d\n", __func__, scd->datapos, len, bytes, scd->datalen); + if (bytes > 0) { + memcpy(buf, scd->datapos, bytes); + scd->datapos += bytes; +@@ -132,18 +131,13 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + dprintf(s, 2, "%s: %d\n", __func__, len); + vmc_register_interface(s); + assert(s->datalen == 0); +- if (s->bufsize < len) { +- s->bufsize = len; +- s->buffer = qemu_realloc(s->buffer, s->bufsize); +- } +- memcpy(s->buffer, buf, len); +- s->datapos = s->buffer; ++ s->datapos = buf; + s->datalen = len; + spice_server_char_device_wakeup(&s->sin); + read_bytes = len - s->datalen; + if (read_bytes != len) { +- dprintf(s, 1, "%s: throttling: %d < %d (%zd)\n", __func__, +- read_bytes, len, s->bufsize); ++ dprintf(s, 1, "%s: throttling: %d < %d\n", __func__, ++ read_bytes, len); + s->chr->write_blocked = true; + /* We'll get passed in the unconsumed data with the next call */ + s->datalen = 0; +-- +1.7.5.1 + diff --git a/0035-usb-redir-Add-flow-control-support.patch b/0035-usb-redir-Add-flow-control-support.patch new file mode 100644 index 0000000..c03c813 --- /dev/null +++ b/0035-usb-redir-Add-flow-control-support.patch @@ -0,0 +1,64 @@ +From f7f2f55e2a8beb68fc81c1def7a0a4436664ed97 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 19 Jul 2011 10:56:19 +0200 +Subject: [PATCH 35/35] usb-redir: Add flow control support + +Signed-off-by: Hans de Goede +--- + usb-redir.c | 28 +++++++++++++++++++++++++++- + 1 files changed, 27 insertions(+), 1 deletions(-) + +diff --git a/usb-redir.c b/usb-redir.c +index e2b5a2c..6932beb 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -224,8 +224,22 @@ static int usbredir_read(void *priv, uint8_t *data, int count) + static int usbredir_write(void *priv, uint8_t *data, int count) + { + USBRedirDevice *dev = priv; ++ int r; + +- return qemu_chr_write(dev->cs, data, count); ++ if (dev->cs->write_blocked) { ++ return 0; ++ } ++ ++ r = qemu_chr_write(dev->cs, data, count); ++ ++ if (r < 0) { ++ if (dev->cs->write_blocked) { ++ return 0; ++ } ++ return -1; ++ } ++ ++ return r; + } + + /* +@@ -782,10 +796,22 @@ static void usbredir_chardev_event(void *opaque, int event) + } + } + ++static void usbredir_chardev_write_unblocked(void *opaque) ++{ ++ USBRedirDevice *dev = opaque; ++ ++ if (dev->parser == NULL) { ++ /* usbredir_open_close_bh hasn't handled the open event yet */ ++ return; ++ } ++ usbredirparser_do_write(dev->parser); ++} ++ + static const QemuChrHandlers usbredir_chr_handlers = { + .fd_can_read = usbredir_chardev_can_read, + .fd_read = usbredir_chardev_read, + .fd_event = usbredir_chardev_event, ++ .fd_write_unblocked = usbredir_chardev_write_unblocked, + }; + + /* +-- +1.7.5.1 + diff --git a/qemu.spec b/qemu.spec index c3e2579..f423c2b 100644 --- a/qemu.spec +++ b/qemu.spec @@ -3,7 +3,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 0.15.0 -Release: 0.1.20110718%githead%{?dist} +Release: 0.2.20110718%githead%{?dist} # Epoch because we pushed a qemu-1.0 package Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD @@ -40,6 +40,46 @@ Source6: ksmtuned.init Source7: ksmtuned Source8: ksmtuned.conf +# Sync with: http://www.kraxel.org/cgit/qemu/log/?h=usb.19 +# USB patches waiting to be pulled by upstream +Patch1: 0001-hw-usb-musb.c-Don-t-misuse-usb_packet_complete.patch +Patch2: 0002-usb-Add-a-usb_fill_port-helper-function.patch +Patch3: 0003-usb-Move-initial-call-of-usb_port_location-to-usb_fi.patch +Patch4: 0004-usb-Add-a-register_companion-USB-bus-op.patch +Patch5: 0005-usb-Make-port-wakeup-and-complete-ops-take-a-USBPort.patch +Patch6: 0006-usb-Replace-device_destroy-bus-op-with-a-child_detac.patch +Patch7: 0007-usb-ehci-drop-unused-num-ports-state-member.patch +Patch8: 0008-usb-ehci-Connect-Status-bit-is-read-only-don-t-allow.patch +Patch9: 0009-usb-ehci-cleanup-port-reset-handling.patch +Patch10: 0010-usb-assert-on-calling-usb_attach-port-NULL-on-a-port.patch +Patch11: 0011-usb-ehci-Fix-handling-of-PED-and-PEDC-port-status-bi.patch +Patch12: 0012-usb-ehci-Add-support-for-registering-companion-contr.patch +Patch13: 0013-usb-uhci-Add-support-for-being-a-companion-controlle.patch +Patch14: 0014-usb-ohci-Add-support-for-being-a-companion-controlle.patch +Patch15: 0015-pci-add-ich9-usb-controller-ids.patch +Patch16: 0016-uhci-add-ich9-controllers.patch +Patch17: 0017-ehci-fix-port-count.patch +Patch18: 0018-ehci-add-ich9-controller.patch +Patch19: 0019-usb-update-documentation.patch +Patch20: 0020-usb_register_port-do-not-set-port-opaque-and-port-in.patch +Patch21: 0021-usb-fixup-bluetooth-descriptors.patch +Patch22: 0022-usb-hub-remove-unused-descriptor-arrays.patch +Patch23: 0023-usb-ohci-raise-interrupt-on-attach.patch +# Add usb-redir device, under review upstream +Patch24: 0024-USB-add-usb-network-redirection-support.patch +# Amit's flow control patches, waiting to glib conversion before going upstream +Patch25: 0025-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch +Patch26: 0026-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch +Patch27: 0027-iohandlers-Add-enable-disable_write_fd_handler-funct.patch +Patch28: 0028-char-Add-framework-for-a-write-unblocked-callback.patch +Patch29: 0029-char-Update-send_all-to-handle-nonblocking-chardev-w.patch +Patch30: 0030-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch +Patch31: 0031-char-Throttle-when-host-connection-is-down.patch +Patch32: 0032-virtio-console-Enable-port-throttling-when-chardev-i.patch +Patch33: 0033-spice-qemu-char.c-add-throttling.patch +Patch34: 0034-spice-qemu-char.c-remove-intermediate-buffer.patch +Patch35: 0035-usb-redir-Add-flow-control-support.patch + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: SDL-devel zlib-devel which texi2html gnutls-devel cyrus-sasl-devel BuildRequires: libaio-devel @@ -212,6 +252,41 @@ such as kvm_stat. %prep %setup -q -n qemu-kvm-%{version} +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 %build # By default we build everything, but allow x86 to build a minimal version @@ -521,6 +596,11 @@ fi %{_mandir}/man1/qemu-img.1* %changelog +* Tue Jul 19 2011 Hans de Goede - 2:0.15.0-0.2.20110718525e3df +- Add support usb redirection over the network, see: + http://fedoraproject.org/wiki/Features/UsbNetworkRedirection +- Restore chardev flow control patches + * Mon Jul 18 2011 Justin M. Forbes - 2:0.15.0-0.1.20110718525e3df - Update to git snapshot as we prepare for 0.15.0 release