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:
commit
b1a6609e75
13
hw/usb/bus.c
13
hw/usb/bus.c
|
@ -37,10 +37,23 @@ static const TypeInfo usb_bus_info = {
|
||||||
static int next_usb_bus = 0;
|
static int next_usb_bus = 0;
|
||||||
static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
|
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 = {
|
const VMStateDescription vmstate_usb_device = {
|
||||||
.name = "USBDevice",
|
.name = "USBDevice",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
|
.post_load = usb_device_post_load,
|
||||||
.fields = (VMStateField []) {
|
.fields = (VMStateField []) {
|
||||||
VMSTATE_UINT8(addr, USBDevice),
|
VMSTATE_UINT8(addr, USBDevice),
|
||||||
VMSTATE_INT32(state, USBDevice),
|
VMSTATE_INT32(state, USBDevice),
|
||||||
|
|
|
@ -414,16 +414,17 @@ struct EHCIState {
|
||||||
*/
|
*/
|
||||||
QEMUTimer *frame_timer;
|
QEMUTimer *frame_timer;
|
||||||
QEMUBH *async_bh;
|
QEMUBH *async_bh;
|
||||||
int astate; // Current state in asynchronous schedule
|
uint32_t astate; /* Current state in asynchronous schedule */
|
||||||
int pstate; // Current state in periodic schedule
|
uint32_t pstate; /* Current state in periodic schedule */
|
||||||
USBPort ports[NB_PORTS];
|
USBPort ports[NB_PORTS];
|
||||||
USBPort *companion_ports[NB_PORTS];
|
USBPort *companion_ports[NB_PORTS];
|
||||||
uint32_t usbsts_pending;
|
uint32_t usbsts_pending;
|
||||||
EHCIQueueHead aqueues;
|
EHCIQueueHead aqueues;
|
||||||
EHCIQueueHead pqueues;
|
EHCIQueueHead pqueues;
|
||||||
|
|
||||||
uint32_t a_fetch_addr; // which address to look at next
|
/* which address to look at next */
|
||||||
uint32_t p_fetch_addr; // which address to look at next
|
uint32_t a_fetch_addr;
|
||||||
|
uint32_t p_fetch_addr;
|
||||||
|
|
||||||
USBPacket ipacket;
|
USBPacket ipacket;
|
||||||
QEMUSGList isgl;
|
QEMUSGList isgl;
|
||||||
|
@ -568,6 +569,7 @@ static inline void ehci_set_interrupt(EHCIState *s, int intr)
|
||||||
level = 1;
|
level = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_usb_ehci_interrupt(level, s->usbsts, s->usbintr);
|
||||||
qemu_set_irq(s->irq, level);
|
qemu_set_irq(s->irq, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,8 +823,9 @@ static void ehci_attach(USBPort *port)
|
||||||
{
|
{
|
||||||
EHCIState *s = port->opaque;
|
EHCIState *s = port->opaque;
|
||||||
uint32_t *portsc = &s->portsc[port->index];
|
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) {
|
if (*portsc & PORTSC_POWNER) {
|
||||||
USBPort *companion = s->companion_ports[port->index];
|
USBPort *companion = s->companion_ports[port->index];
|
||||||
|
@ -841,8 +844,9 @@ static void ehci_detach(USBPort *port)
|
||||||
{
|
{
|
||||||
EHCIState *s = port->opaque;
|
EHCIState *s = port->opaque;
|
||||||
uint32_t *portsc = &s->portsc[port->index];
|
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) {
|
if (*portsc & PORTSC_POWNER) {
|
||||||
USBPort *companion = s->companion_ports[port->index];
|
USBPort *companion = s->companion_ports[port->index];
|
||||||
|
@ -2390,9 +2394,58 @@ static USBBusOps ehci_bus_ops = {
|
||||||
.register_companion = ehci_register_companion,
|
.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 = {
|
static const VMStateDescription vmstate_ehci = {
|
||||||
.name = "ehci",
|
.name = "ehci",
|
||||||
.unmigratable = 1,
|
.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[] = {
|
static Property ehci_properties[] = {
|
||||||
|
|
|
@ -292,10 +292,10 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
||||||
|
|
||||||
static void uhci_async_cancel_all(UHCIState *s)
|
static void uhci_async_cancel_all(UHCIState *s)
|
||||||
{
|
{
|
||||||
UHCIQueue *queue;
|
UHCIQueue *queue, *nq;
|
||||||
UHCIAsync *curr, *n;
|
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) {
|
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
||||||
uhci_async_unlink(curr);
|
uhci_async_unlink(curr);
|
||||||
uhci_async_cancel(curr);
|
uhci_async_cancel(curr);
|
||||||
|
|
|
@ -111,6 +111,7 @@ typedef struct USBHostDevice {
|
||||||
uint32_t iso_urb_count;
|
uint32_t iso_urb_count;
|
||||||
uint32_t options;
|
uint32_t options;
|
||||||
Notifier exit;
|
Notifier exit;
|
||||||
|
QEMUBH *bh;
|
||||||
|
|
||||||
struct endp_data ep_in[USB_MAX_ENDPOINTS];
|
struct endp_data ep_in[USB_MAX_ENDPOINTS];
|
||||||
struct endp_data ep_out[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)
|
static int usb_host_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, 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);
|
QTAILQ_INSERT_TAIL(&hostdevs, s, next);
|
||||||
s->exit.notify = usb_host_exit_notifier;
|
s->exit.notify = usb_host_exit_notifier;
|
||||||
qemu_add_exit_notifier(&s->exit);
|
qemu_add_exit_notifier(&s->exit);
|
||||||
|
s->bh = qemu_bh_new(usb_host_post_load_bh, s);
|
||||||
usb_host_auto_check(NULL);
|
usb_host_auto_check(NULL);
|
||||||
|
|
||||||
if (s->match.bus_num != 0 && s->match.port != 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 = {
|
static const VMStateDescription vmstate_usb_host = {
|
||||||
.name = "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[] = {
|
static Property usb_host_dev_properties[] = {
|
||||||
|
@ -1737,6 +1782,7 @@ static void usb_host_auto_check(void *unused)
|
||||||
struct USBHostDevice *s;
|
struct USBHostDevice *s;
|
||||||
int unconnected = 0;
|
int unconnected = 0;
|
||||||
|
|
||||||
|
if (runstate_is_running()) {
|
||||||
usb_host_scan(NULL, usb_host_auto_scan);
|
usb_host_scan(NULL, usb_host_auto_scan);
|
||||||
|
|
||||||
QTAILQ_FOREACH(s, &hostdevs, next) {
|
QTAILQ_FOREACH(s, &hostdevs, next) {
|
||||||
|
@ -1757,6 +1803,7 @@ static void usb_host_auto_check(void *unused)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!usb_auto_timer) {
|
if (!usb_auto_timer) {
|
||||||
usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
|
usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
|
||||||
|
|
|
@ -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_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_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_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_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s"
|
||||||
usb_ehci_port_detach(uint32_t port) "detach port #%d"
|
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_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_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_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_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
|
# hw/usb/hcd-uhci.c
|
||||||
usb_uhci_reset(void) "=== RESET ==="
|
usb_uhci_reset(void) "=== RESET ==="
|
||||||
|
|
Loading…
Reference in a new issue