diff options
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | hw/usb/bus.c | 11 | ||||
-rw-r--r-- | hw/usb/hcd-ehci-pci.c | 14 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 29 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.h | 2 | ||||
-rw-r--r-- | hw/usb/hcd-ohci.c | 27 | ||||
-rw-r--r-- | hw/usb/hcd-uhci.c | 24 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.c | 50 | ||||
-rw-r--r-- | include/hw/usb.h | 1 | ||||
-rw-r--r-- | tests/Makefile | 10 | ||||
-rw-r--r-- | tests/usb-hcd-ohci-test.c | 35 | ||||
-rw-r--r-- | tests/usb-hcd-uhci-test.c | 35 | ||||
-rw-r--r-- | tests/usb-hcd-xhci-test.c | 35 | ||||
-rw-r--r-- | trace-events | 3 |
14 files changed, 264 insertions, 14 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 59940f9..a74c04c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -614,7 +614,7 @@ USB M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: hw/usb/* -F: tests/usb-hcd-ehci-test.c +F: tests/usb-*-test.c VFIO M: Alex Williamson <alex.williamson@redhat.com> diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 927a47b..c7c4dad 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -87,6 +87,13 @@ void usb_bus_new(USBBus *bus, size_t bus_size, QTAILQ_INSERT_TAIL(&busses, bus, next); } +void usb_bus_release(USBBus *bus) +{ + assert(next_usb_bus > 0); + + QTAILQ_REMOVE(&busses, bus, next); +} + USBBus *usb_bus_find(int busnr) { USBBus *bus; @@ -589,11 +596,11 @@ static char *usb_get_fw_dev_path(DeviceState *qdev) nr = strtol(in, &in, 10); if (in[0] == '.') { /* some hub between root port and device */ - pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr); + pos += snprintf(fw_path + pos, fw_len - pos, "hub@%lx/", nr); in++; } else { /* the device itself */ - pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld", + pos += snprintf(fw_path + pos, fw_len - pos, "%s@%lx", qdev_fw_name(qdev), nr); break; } diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 505741a..289ca3b 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -84,6 +84,19 @@ static void usb_ehci_pci_init(Object *obj) usb_ehci_init(s, DEVICE(obj)); } +static void usb_ehci_pci_exit(PCIDevice *dev) +{ + EHCIPCIState *i = PCI_EHCI(dev); + EHCIState *s = &i->ehci; + + usb_ehci_unrealize(s, DEVICE(dev), NULL); + + if (s->irq) { + g_free(s->irq); + s->irq = NULL; + } +} + static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int l) { @@ -121,6 +134,7 @@ static void ehci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = usb_ehci_pci_initfn; + k->exit = usb_ehci_pci_exit; k->class_id = PCI_CLASS_SERIAL_USB; k->config_write = usb_ehci_pci_write_config; dc->hotpluggable = false; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 448e007..bacb7ce 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2468,7 +2468,34 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->device = dev; qemu_register_reset(ehci_reset, s); - qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); + s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); +} + +void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp) +{ + trace_usb_ehci_unrealize(); + + if (s->frame_timer) { + timer_del(s->frame_timer); + timer_free(s->frame_timer); + s->frame_timer = NULL; + } + if (s->async_bh) { + qemu_bh_delete(s->async_bh); + } + + ehci_queues_rip_all(s, 0); + ehci_queues_rip_all(s, 1); + + memory_region_del_subregion(&s->mem, &s->mem_caps); + memory_region_del_subregion(&s->mem, &s->mem_opreg); + memory_region_del_subregion(&s->mem, &s->mem_ports); + + usb_bus_release(&s->bus); + + if (s->vmstate) { + qemu_del_vm_change_state_handler(s->vmstate); + } } void usb_ehci_init(EHCIState *s, DeviceState *dev) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 1ad4b96..4858b7e 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -316,12 +316,14 @@ struct EHCIState { uint32_t async_stepdown; uint32_t periodic_sched_active; bool int_req_by_async; + VMChangeStateEntry *vmstate; }; extern const VMStateDescription vmstate_ehci; void usb_ehci_init(EHCIState *s, DeviceState *dev); void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp); +void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp); #define TYPE_PCI_EHCI "pci-ehci-usb" #define PCI_EHCI(obj) OBJECT_CHECK(EHCIPCIState, (obj), TYPE_PCI_EHCI) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 13afdf5..83bec34 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -619,8 +619,8 @@ static inline int ohci_put_td(OHCIState *ohci, static inline int ohci_put_iso_td(OHCIState *ohci, dma_addr_t addr, struct ohci_iso_td *td) { - return put_dwords(ohci, addr, (uint32_t *)td, 4 || - put_words(ohci, addr + 16, td->offset, 8)); + return put_dwords(ohci, addr, (uint32_t *)td, 4) || + put_words(ohci, addr + 16, td->offset, 8); } static inline int ohci_put_hcca(OHCIState *ohci, @@ -1371,8 +1371,10 @@ static int ohci_bus_start(OHCIState *ohci) /* Stop sending SOF tokens on the bus */ static void ohci_bus_stop(OHCIState *ohci) { - if (ohci->eof_timer) + if (ohci->eof_timer) { timer_del(ohci->eof_timer); + timer_free(ohci->eof_timer); + } ohci->eof_timer = NULL; } @@ -1952,6 +1954,24 @@ static int usb_ohci_initfn_pci(PCIDevice *dev) return 0; } +static void usb_ohci_exit(PCIDevice *dev) +{ + OHCIPCIState *ohci = PCI_OHCI(dev); + OHCIState *s = &ohci->state; + + ohci_bus_stop(s); + + if (s->async_td) { + usb_cancel_packet(&s->usb_packet); + s->async_td = 0; + } + ohci_stop_endpoints(s); + + if (!ohci->masterbus) { + usb_bus_release(&s->bus); + } +} + #define TYPE_SYSBUS_OHCI "sysbus-ohci" #define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) @@ -2089,6 +2109,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = usb_ohci_initfn_pci; + k->exit = usb_ohci_exit; k->vendor_id = PCI_VENDOR_ID_APPLE; k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB; k->class_id = PCI_CLASS_SERIAL_USB; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index ee5f112..3b3ebcd 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1256,6 +1256,29 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) return usb_uhci_common_initfn(dev); } +static void usb_uhci_exit(PCIDevice *dev) +{ + UHCIState *s = DO_UPCAST(UHCIState, dev, dev); + + trace_usb_uhci_exit(); + + if (s->frame_timer) { + timer_del(s->frame_timer); + timer_free(s->frame_timer); + s->frame_timer = NULL; + } + + if (s->bh) { + qemu_bh_delete(s->bh); + } + + uhci_async_cancel_all(s); + + if (!s->masterbus) { + usb_bus_release(&s->bus); + } +} + static Property uhci_properties[] = { DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), @@ -1272,6 +1295,7 @@ static void uhci_class_init(ObjectClass *klass, void *data) UHCIInfo *info = data; k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; + k->exit = info->unplug ? usb_uhci_exit : NULL; k->vendor_id = info->vendor_id; k->device_id = info->device_id; k->revision = info->revision; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 58c4b11..bbe4c5f 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1151,7 +1151,7 @@ static void xhci_reset_streams(XHCIEPContext *epctx) static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) { assert(epctx->pstreams == NULL); - epctx->nr_pstreams = 2 << (epctx->max_pstreams + 1); + epctx->nr_pstreams = 2 << epctx->max_pstreams; epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); } @@ -1180,7 +1180,7 @@ static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, slot = &xhci->slots[slotid - 1]; for (i = 2, j = 0; i <= 31; i++) { - if (!(epmask & (1 << i))) { + if (!(epmask & (1u << i))) { continue; } @@ -1380,14 +1380,11 @@ static void xhci_init_epctx(XHCIEPContext *epctx, dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]); epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK; - DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type); epctx->pctx = pctx; epctx->max_psize = ctx[1]>>16; epctx->max_psize *= 1+((ctx[1]>>8)&0xff); epctx->max_pstreams = (ctx[0] >> 10) & 0xf; epctx->lsa = (ctx[0] >> 15) & 1; - DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n", - epid/2, epid%2, epctx->max_psize); if (epctx->max_pstreams) { xhci_alloc_streams(epctx, dequeue); } else { @@ -1418,6 +1415,9 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, slot->eps[epid-1] = epctx; xhci_init_epctx(epctx, pctx, ctx); + DPRINTF("xhci: endpoint %d.%d type is %d, max transaction (burst) " + "size is %d\n", epid/2, epid%2, epctx->type, epctx->max_psize); + epctx->mfindex_last = 0; epctx->state = EP_RUNNING; @@ -2465,7 +2465,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); if (res != CC_SUCCESS) { for (i = 2; i <= 31; i++) { - if (ictl_ctx[1] & (1 << i)) { + if (ictl_ctx[1] & (1u << i)) { xhci_disable_ep(xhci, slotid, i); } } @@ -3644,6 +3644,43 @@ static int usb_xhci_initfn(struct PCIDevice *dev) return 0; } +static void usb_xhci_exit(PCIDevice *dev) +{ + int i; + XHCIState *xhci = XHCI(dev); + + trace_usb_xhci_exit(); + + for (i = 0; i < xhci->numslots; i++) { + xhci_disable_slot(xhci, i + 1); + } + + if (xhci->mfwrap_timer) { + timer_del(xhci->mfwrap_timer); + timer_free(xhci->mfwrap_timer); + xhci->mfwrap_timer = NULL; + } + + memory_region_del_subregion(&xhci->mem, &xhci->mem_cap); + memory_region_del_subregion(&xhci->mem, &xhci->mem_oper); + memory_region_del_subregion(&xhci->mem, &xhci->mem_runtime); + memory_region_del_subregion(&xhci->mem, &xhci->mem_doorbell); + + for (i = 0; i < xhci->numports; i++) { + XHCIPort *port = &xhci->ports[i]; + memory_region_del_subregion(&xhci->mem, &port->mem); + } + + /* destroy msix memory region */ + if (dev->msix_table && dev->msix_pba + && dev->msix_entry_used) { + memory_region_del_subregion(&xhci->mem, &dev->msix_table_mmio); + memory_region_del_subregion(&xhci->mem, &dev->msix_pba_mmio); + } + + usb_bus_release(&xhci->bus); +} + static int usb_xhci_post_load(void *opaque, int version_id) { XHCIState *xhci = opaque; @@ -3836,6 +3873,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_USB, dc->categories); k->init = usb_xhci_initfn; + k->exit = usb_xhci_exit; k->vendor_id = PCI_VENDOR_ID_NEC; k->device_id = PCI_DEVICE_ID_NEC_UPD720200; k->class_id = PCI_CLASS_SERIAL_USB; diff --git a/include/hw/usb.h b/include/hw/usb.h index 223a5ae..6b32a3b 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -529,6 +529,7 @@ struct USBBusOps { void usb_bus_new(USBBus *bus, size_t bus_size, USBBusOps *ops, DeviceState *host); +void usb_bus_release(USBBus *bus); USBBus *usb_bus_find(int busnr); void usb_legacy_register(const char *typename, const char *usbdevice_name, USBDevice *(*usbdevice_init)(USBBus *bus, diff --git a/tests/Makefile b/tests/Makefile index 837e9c8..469c0a5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -155,11 +155,16 @@ check-qtest-i386-y += tests/i82801b11-test$(EXESUF) gcov-files-i386-y += hw/pci-bridge/i82801b11.c check-qtest-i386-y += tests/ioh3420-test$(EXESUF) gcov-files-i386-y += hw/pci-bridge/ioh3420.c +check-qtest-i386-y += tests/usb-hcd-ohci-test$(EXESUF) +gcov-files-i386-y += hw/usb/hcd-ohci.c +check-qtest-i386-y += tests/usb-hcd-uhci-test$(EXESUF) +gcov-files-i386-y += hw/usb/hcd-uhci.c check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF) gcov-files-i386-y += hw/usb/hcd-ehci.c -gcov-files-i386-y += hw/usb/hcd-uhci.c gcov-files-i386-y += hw/usb/dev-hid.c gcov-files-i386-y += hw/usb/dev-storage.c +check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF) +gcov-files-i386-y += hw/usb/hcd-xhci.c check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c @@ -335,7 +340,10 @@ tests/ac97-test$(EXESUF): tests/ac97-test.o tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o +tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o +tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y) +tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c new file mode 100644 index 0000000..fbc3ffe --- /dev/null +++ b/tests/usb-hcd-ohci-test.c @@ -0,0 +1,35 @@ +/* + * QTest testcase for USB OHCI controller + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <string.h> +#include "libqtest.h" +#include "qemu/osdep.h" + + +static void test_ohci_init(void) +{ + qtest_start("-device pci-ohci,id=ohci"); + + qtest_end(); +} + + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/ohci/pci/init", test_ohci_init); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c new file mode 100644 index 0000000..94e858f --- /dev/null +++ b/tests/usb-hcd-uhci-test.c @@ -0,0 +1,35 @@ +/* + * QTest testcase for USB UHCI controller + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <string.h> +#include "libqtest.h" +#include "qemu/osdep.h" + + +static void test_uhci_init(void) +{ + qtest_start("-device piix3-usb-uhci,id=uhci"); + + qtest_end(); +} + + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/uhci/pci/init", test_uhci_init); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c new file mode 100644 index 0000000..743e979 --- /dev/null +++ b/tests/usb-hcd-xhci-test.c @@ -0,0 +1,35 @@ +/* + * QTest testcase for USB xHCI controller + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <string.h> +#include "libqtest.h" +#include "qemu/osdep.h" + + +static void test_xhci_init(void) +{ + qtest_start("-device nec-usb-xhci,id=xhci"); + + qtest_end(); +} + + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/xhci/pci/init", test_xhci_init); + + ret = g_test_run(); + + return ret; +} diff --git a/trace-events b/trace-events index 81bc915..03ac5d2 100644 --- a/trace-events +++ b/trace-events @@ -297,6 +297,7 @@ usb_port_release(int bus, const char *port) "bus %d, port %s" # hw/usb/hcd-ehci.c usb_ehci_reset(void) "=== RESET ===" +usb_ehci_unrealize(void) "=== UNREALIZE ===" usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x" usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" @@ -329,6 +330,7 @@ usb_ehci_dma_error(void) "" # hw/usb/hcd-uhci.c usb_uhci_reset(void) "=== RESET ===" +usb_uhci_exit(void) "=== EXIT ===" usb_uhci_schedule_start(void) "" usb_uhci_schedule_stop(void) "" usb_uhci_frame_start(uint32_t num) "nr %d" @@ -358,6 +360,7 @@ usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" # hw/usb/hcd-xhci.c usb_xhci_reset(void) "=== RESET ===" +usb_xhci_exit(void) "=== EXIT ===" usb_xhci_run(void) "" usb_xhci_stop(void) "" usb_xhci_cap_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" |