Merge remote-tracking branch 'kraxel/usb.54' into staging

* kraxel/usb.54:
  uhci: fix uhci_async_cancel_all
  usb-host: live migration support
  usb-host: attach only to running guest
  ehci: tracing improvements
  usb: restore USBDevice->attached on vmload
  ehci: add live migration support
This commit is contained in:
Anthony Liguori 2012-06-26 15:01:47 -05:00
commit b1a6609e75
5 changed files with 142 additions and 28 deletions

View file

@ -37,10 +37,23 @@ static const TypeInfo usb_bus_info = {
static int next_usb_bus = 0;
static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
static int usb_device_post_load(void *opaque, int version_id)
{
USBDevice *dev = opaque;
if (dev->state == USB_STATE_NOTATTACHED) {
dev->attached = 0;
} else {
dev->attached = 1;
}
return 0;
}
const VMStateDescription vmstate_usb_device = {
.name = "USBDevice",
.version_id = 1,
.minimum_version_id = 1,
.post_load = usb_device_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT8(addr, USBDevice),
VMSTATE_INT32(state, USBDevice),

View file

@ -414,16 +414,17 @@ struct EHCIState {
*/
QEMUTimer *frame_timer;
QEMUBH *async_bh;
int astate; // Current state in asynchronous schedule
int pstate; // Current state in periodic schedule
uint32_t astate; /* Current state in asynchronous schedule */
uint32_t pstate; /* Current state in periodic schedule */
USBPort ports[NB_PORTS];
USBPort *companion_ports[NB_PORTS];
uint32_t usbsts_pending;
EHCIQueueHead aqueues;
EHCIQueueHead pqueues;
uint32_t a_fetch_addr; // which address to look at next
uint32_t p_fetch_addr; // which address to look at next
/* which address to look at next */
uint32_t a_fetch_addr;
uint32_t p_fetch_addr;
USBPacket ipacket;
QEMUSGList isgl;
@ -568,6 +569,7 @@ static inline void ehci_set_interrupt(EHCIState *s, int intr)
level = 1;
}
trace_usb_ehci_interrupt(level, s->usbsts, s->usbintr);
qemu_set_irq(s->irq, level);
}
@ -821,8 +823,9 @@ static void ehci_attach(USBPort *port)
{
EHCIState *s = port->opaque;
uint32_t *portsc = &s->portsc[port->index];
const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci";
trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
trace_usb_ehci_port_attach(port->index, owner, port->dev->product_desc);
if (*portsc & PORTSC_POWNER) {
USBPort *companion = s->companion_ports[port->index];
@ -841,8 +844,9 @@ static void ehci_detach(USBPort *port)
{
EHCIState *s = port->opaque;
uint32_t *portsc = &s->portsc[port->index];
const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci";
trace_usb_ehci_port_detach(port->index);
trace_usb_ehci_port_detach(port->index, owner);
if (*portsc & PORTSC_POWNER) {
USBPort *companion = s->companion_ports[port->index];
@ -2390,9 +2394,58 @@ static USBBusOps ehci_bus_ops = {
.register_companion = ehci_register_companion,
};
static int usb_ehci_post_load(void *opaque, int version_id)
{
EHCIState *s = opaque;
int i;
for (i = 0; i < NB_PORTS; i++) {
USBPort *companion = s->companion_ports[i];
if (companion == NULL) {
continue;
}
if (s->portsc[i] & PORTSC_POWNER) {
companion->dev = s->ports[i].dev;
} else {
companion->dev = NULL;
}
}
return 0;
}
static const VMStateDescription vmstate_ehci = {
.name = "ehci",
.unmigratable = 1,
.name = "ehci",
.version_id = 1,
.post_load = usb_ehci_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, EHCIState),
/* mmio registers */
VMSTATE_UINT32(usbcmd, EHCIState),
VMSTATE_UINT32(usbsts, EHCIState),
VMSTATE_UINT32(usbintr, EHCIState),
VMSTATE_UINT32(frindex, EHCIState),
VMSTATE_UINT32(ctrldssegment, EHCIState),
VMSTATE_UINT32(periodiclistbase, EHCIState),
VMSTATE_UINT32(asynclistaddr, EHCIState),
VMSTATE_UINT32(configflag, EHCIState),
VMSTATE_UINT32(portsc[0], EHCIState),
VMSTATE_UINT32(portsc[1], EHCIState),
VMSTATE_UINT32(portsc[2], EHCIState),
VMSTATE_UINT32(portsc[3], EHCIState),
VMSTATE_UINT32(portsc[4], EHCIState),
VMSTATE_UINT32(portsc[5], EHCIState),
/* frame timer */
VMSTATE_TIMER(frame_timer, EHCIState),
VMSTATE_UINT64(last_run_ns, EHCIState),
VMSTATE_UINT32(async_stepdown, EHCIState),
/* schedule state */
VMSTATE_UINT32(astate, EHCIState),
VMSTATE_UINT32(pstate, EHCIState),
VMSTATE_UINT32(a_fetch_addr, EHCIState),
VMSTATE_UINT32(p_fetch_addr, EHCIState),
VMSTATE_END_OF_LIST()
}
};
static Property ehci_properties[] = {

View file

@ -292,10 +292,10 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
static void uhci_async_cancel_all(UHCIState *s)
{
UHCIQueue *queue;
UHCIQueue *queue, *nq;
UHCIAsync *curr, *n;
QTAILQ_FOREACH(queue, &s->queues, next) {
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) {
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
uhci_async_unlink(curr);
uhci_async_cancel(curr);

View file

@ -111,6 +111,7 @@ typedef struct USBHostDevice {
uint32_t iso_urb_count;
uint32_t options;
Notifier exit;
QEMUBH *bh;
struct endp_data ep_in[USB_MAX_ENDPOINTS];
struct endp_data ep_out[USB_MAX_ENDPOINTS];
@ -1421,6 +1422,43 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data)
}
}
/*
* This is *NOT* about restoring state. We have absolutely no idea
* what state the host device is in at the moment and whenever it is
* still present in the first place. Attemping to contine where we
* left off is impossible.
*
* What we are going to to to here is emulate a surprise removal of
* the usb device passed through, then kick host scan so the device
* will get re-attached (and re-initialized by the guest) in case it
* is still present.
*
* As the device removal will change the state of other devices (usb
* host controller, most likely interrupt controller too) we have to
* wait with it until *all* vmstate is loaded. Thus post_load just
* kicks a bottom half which then does the actual work.
*/
static void usb_host_post_load_bh(void *opaque)
{
USBHostDevice *dev = opaque;
if (dev->fd != -1) {
usb_host_close(dev);
}
if (dev->dev.attached) {
usb_device_detach(&dev->dev);
}
usb_host_auto_check(NULL);
}
static int usb_host_post_load(void *opaque, int version_id)
{
USBHostDevice *dev = opaque;
qemu_bh_schedule(dev->bh);
return 0;
}
static int usb_host_initfn(USBDevice *dev)
{
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
@ -1432,6 +1470,7 @@ static int usb_host_initfn(USBDevice *dev)
QTAILQ_INSERT_TAIL(&hostdevs, s, next);
s->exit.notify = usb_host_exit_notifier;
qemu_add_exit_notifier(&s->exit);
s->bh = qemu_bh_new(usb_host_post_load_bh, s);
usb_host_auto_check(NULL);
if (s->match.bus_num != 0 && s->match.port != NULL) {
@ -1443,7 +1482,13 @@ static int usb_host_initfn(USBDevice *dev)
static const VMStateDescription vmstate_usb_host = {
.name = "usb-host",
.unmigratable = 1,
.version_id = 1,
.minimum_version_id = 1,
.post_load = usb_host_post_load,
.fields = (VMStateField[]) {
VMSTATE_USB_DEVICE(dev, USBHostDevice),
VMSTATE_END_OF_LIST()
}
};
static Property usb_host_dev_properties[] = {
@ -1737,25 +1782,27 @@ static void usb_host_auto_check(void *unused)
struct USBHostDevice *s;
int unconnected = 0;
usb_host_scan(NULL, usb_host_auto_scan);
if (runstate_is_running()) {
usb_host_scan(NULL, usb_host_auto_scan);
QTAILQ_FOREACH(s, &hostdevs, next) {
if (s->fd == -1) {
unconnected++;
QTAILQ_FOREACH(s, &hostdevs, next) {
if (s->fd == -1) {
unconnected++;
}
if (s->seen == 0) {
s->errcount = 0;
}
s->seen = 0;
}
if (s->seen == 0) {
s->errcount = 0;
}
s->seen = 0;
}
if (unconnected == 0) {
/* nothing to watch */
if (usb_auto_timer) {
qemu_del_timer(usb_auto_timer);
trace_usb_host_auto_scan_disabled();
if (unconnected == 0) {
/* nothing to watch */
if (usb_auto_timer) {
qemu_del_timer(usb_auto_timer);
trace_usb_host_auto_scan_disabled();
}
return;
}
return;
}
if (!usb_auto_timer) {

View file

@ -252,12 +252,13 @@ usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QT
usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d"
usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %08x - active %d"
usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s"
usb_ehci_port_detach(uint32_t port) "detach port #%d"
usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s"
usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s"
usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d"
usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
usb_ehci_interrupt(uint32_t level, uint32_t sts, uint32_t mask) "level %d, sts 0x%x, mask 0x%x"
# hw/usb/hcd-uhci.c
usb_uhci_reset(void) "=== RESET ==="