X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=hw%2Fusb%2Fhcd-ohci.c;h=1e6e85e86a8d721618724013763dd9b1d60d829c;hb=3c6ef471ee67bf5a22a9e0ecfdc45ca7d2393216;hp=18b31022a71b0ef9be15094865abf8973d998559;hpb=e4f4fb1eca795e36f363b4647724221e774523c1;p=mirror_qemu.git diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 18b31022a7..1e6e85e86a 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -26,90 +26,26 @@ */ #include "qemu/osdep.h" -#include "hw/hw.h" +#include "hw/irq.h" #include "qapi/error.h" +#include "qemu/module.h" #include "qemu/timer.h" #include "hw/usb.h" -#include "hw/pci/pci.h" +#include "migration/vmstate.h" #include "hw/sysbus.h" #include "hw/qdev-dma.h" +#include "hw/qdev-properties.h" #include "trace.h" +#include "hcd-ohci.h" /* This causes frames to occur 1000x slower */ //#define OHCI_TIME_WARP 1 -/* Number of Downstream Ports on the root hub. */ - -#define OHCI_MAX_PORTS 15 - #define ED_LINK_LIMIT 32 static int64_t usb_frame_time; static int64_t usb_bit_time; -typedef struct OHCIPort { - USBPort port; - uint32_t ctrl; -} OHCIPort; - -typedef struct { - USBBus bus; - qemu_irq irq; - MemoryRegion mem; - AddressSpace *as; - int num_ports; - const char *name; - - QEMUTimer *eof_timer; - int64_t sof_time; - - /* OHCI state */ - /* Control partition */ - uint32_t ctl, status; - uint32_t intr_status; - uint32_t intr; - - /* memory pointer partition */ - uint32_t hcca; - uint32_t ctrl_head, ctrl_cur; - uint32_t bulk_head, bulk_cur; - uint32_t per_cur; - uint32_t done; - int32_t done_count; - - /* Frame counter partition */ - uint16_t fsmps; - uint8_t fit; - uint16_t fi; - uint8_t frt; - uint16_t frame_number; - uint16_t padding; - uint32_t pstart; - uint32_t lst; - - /* Root Hub partition */ - uint32_t rhdesc_a, rhdesc_b; - uint32_t rhstatus; - OHCIPort rhport[OHCI_MAX_PORTS]; - - /* PXA27x Non-OHCI events */ - uint32_t hstatus; - uint32_t hmask; - uint32_t hreset; - uint32_t htest; - - /* SM501 local memory offset */ - dma_addr_t localmem_base; - - /* Active packets. */ - uint32_t old_ctl; - USBPacket usb_packet; - uint8_t usb_buf[8192]; - uint32_t async_td; - bool async_complete; - -} OHCIState; - /* Host Controller Communications Area */ struct ohci_hcca { uint32_t intr[32]; @@ -122,7 +58,6 @@ struct ohci_hcca { #define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) #define ED_WBACK_SIZE 4 -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. */ @@ -302,7 +237,10 @@ struct ohci_iso_td { #define OHCI_HRESET_FSBIR (1 << 0) -static void ohci_die(OHCIState *ohci); +static void ohci_die(OHCIState *ohci) +{ + ohci->ohci_die(ohci); +} /* Update IRQ levels */ static inline void ohci_intr_update(OHCIState *ohci) @@ -426,7 +364,7 @@ static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) return NULL; } -static void ohci_stop_endpoints(OHCIState *ohci) +void ohci_stop_endpoints(OHCIState *ohci) { USBDevice *dev; int i, j; @@ -498,7 +436,7 @@ static void ohci_soft_reset(OHCIState *ohci) ohci->lst = OHCI_LS_THRESH; } -static void ohci_hard_reset(OHCIState *ohci) +void ohci_hard_reset(OHCIState *ohci) { ohci_soft_reset(ohci); ohci->ctl = 0; @@ -848,6 +786,10 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, bool int_req = relative_frame_number == frame_count && OHCI_BM(iso_td.flags, TD_DI) == 0; dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) { + trace_usb_ohci_td_dev_error(); + return 1; + } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); @@ -936,16 +878,18 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, return 1; } -#ifdef trace_event_get_state static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) { - bool print16 = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_SHORT); - bool printall = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_FULL); + bool print16; + bool printall; const int width = 16; int i; char tmp[3 * width + 1]; char *p = tmp; + print16 = !!trace_event_get_state_backends(TRACE_USB_OHCI_TD_PKT_SHORT); + printall = !!trace_event_get_state_backends(TRACE_USB_OHCI_TD_PKT_FULL); + if (!printall && !print16) { return; } @@ -967,11 +911,6 @@ static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) p += sprintf(p, " %.2x", buf[i]); } } -#else -static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) -{ -} -#endif /* Service a transport descriptor. Returns nonzero to terminate processing of this endpoint. */ @@ -1074,6 +1013,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) return 1; } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) { + trace_usb_ohci_td_dev_error(); + return 1; + } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, OHCI_BM(td.flags, TD_DI) == 0); @@ -1159,6 +1102,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_EC, 3); break; } + /* An error occured so we have to clear the interrupt counter. See + * spec at 6.4.4 on page 104 */ + ohci->done_count = 0; } ed->head |= OHCI_ED_H; } @@ -1192,7 +1138,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) if (head == 0) return 0; - for (cur = head; cur; cur = next_ed) { + for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); ohci_die(ohci); @@ -1201,11 +1147,6 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) next_ed = ed.next & OHCI_DPTR_MASK; - if (++link_cnt > ED_LINK_LIMIT) { - ohci_die(ohci); - return 0; - } - if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; /* Cancel pending packets for ED that have been paused. */ @@ -1253,12 +1194,12 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) /* set a timer for EOF */ static void ohci_eof_timer(OHCIState *ohci) { - ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); timer_mod(ohci->eof_timer, ohci->sof_time + usb_frame_time); } /* Set a timer for EOF and generate a SOF event */ static void ohci_sof(OHCIState *ohci) { + ohci->sof_time += usb_frame_time; ohci_eof_timer(ohci); ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1362,13 +1303,14 @@ static int ohci_bus_start(OHCIState *ohci) * can meet some race conditions */ + ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ohci_eof_timer(ohci); return 1; } /* Stop sending SOF tokens on the bus */ -static void ohci_bus_stop(OHCIState *ohci) +void ohci_bus_stop(OHCIState *ohci) { trace_usb_ohci_stop(ohci->name); timer_del(ohci->eof_timer); @@ -1476,6 +1418,9 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) * set already. */ tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; + if (tks < 0) { + tks = 0; + } /* avoid muldiv if possible */ if (tks >= usb_frame_time) @@ -1845,18 +1790,19 @@ static USBPortOps ohci_port_ops = { static USBBusOps ohci_bus_ops = { }; -static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, dma_addr_t localmem_base, - char *masterbus, uint32_t firstport, - AddressSpace *as, Error **errp) +void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, + dma_addr_t localmem_base, char *masterbus, + uint32_t firstport, AddressSpace *as, + void (*ohci_die_fn)(struct OHCIState *), Error **errp) { Error *err = NULL; int i; ohci->as = as; + ohci->ohci_die = ohci_die_fn; if (num_ports > OHCI_MAX_PORTS) { - error_setg(errp, "OHCI num-ports=%d is too big (limit is %d ports)", + error_setg(errp, "OHCI num-ports=%u is too big (limit is %u ports)", num_ports, OHCI_MAX_PORTS); return; } @@ -1912,108 +1858,31 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, ohci_frame_boundary, ohci); } -#define TYPE_PCI_OHCI "pci-ohci" -#define PCI_OHCI(obj) OBJECT_CHECK(OHCIPCIState, (obj), TYPE_PCI_OHCI) - -typedef struct { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - OHCIState state; - char *masterbus; - uint32_t num_ports; - uint32_t firstport; -} OHCIPCIState; - -/** A typical O/EHCI will stop operating, set itself into error state - * (which can be queried by MMIO) and will set PERR in its config - * space to signal that it got an error +/** + * A typical OHCI will stop operating and set itself into error state + * (which can be queried by MMIO) to signal that it got an error. */ -static void ohci_die(OHCIState *ohci) +void ohci_sysbus_die(struct OHCIState *ohci) { - OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state); - trace_usb_ohci_die(); ohci_set_interrupt(ohci, OHCI_INTR_UE); ohci_bus_stop(ohci); - pci_set_word(dev->parent_obj.config + PCI_STATUS, - PCI_STATUS_DETECTED_PARITY); } -static void usb_ohci_realize_pci(PCIDevice *dev, Error **errp) +static void ohci_realize_pxa(DeviceState *dev, Error **errp) { + OHCISysBusState *s = SYSBUS_OHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *err = NULL; - OHCIPCIState *ohci = PCI_OHCI(dev); - dev->config[PCI_CLASS_PROG] = 0x10; /* OHCI */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ - - usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0, - ohci->masterbus, ohci->firstport, - pci_get_address_space(dev), &err); + usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, + s->masterbus, s->firstport, + &address_space_memory, ohci_sysbus_die, &err); if (err) { error_propagate(errp, err); return; } - - ohci->state.irq = pci_allocate_irq(dev); - pci_register_bar(dev, 0, 0, &ohci->state.mem); -} - -static void usb_ohci_exit(PCIDevice *dev) -{ - OHCIPCIState *ohci = PCI_OHCI(dev); - OHCIState *s = &ohci->state; - - trace_usb_ohci_exit(s->name); - 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); - } - - timer_del(s->eof_timer); - timer_free(s->eof_timer); -} - -static void usb_ohci_reset_pci(DeviceState *d) -{ - PCIDevice *dev = PCI_DEVICE(d); - OHCIPCIState *ohci = PCI_OHCI(dev); - OHCIState *s = &ohci->state; - - ohci_hard_reset(s); -} - -#define TYPE_SYSBUS_OHCI "sysbus-ohci" -#define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - OHCIState ohci; - uint32_t num_ports; - dma_addr_t dma_offset; -} OHCISysBusState; - -static void ohci_realize_pxa(DeviceState *dev, Error **errp) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - /* Cannot fail as we pass NULL for masterbus */ - usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, NULL, 0, - &address_space_memory, &error_abort); sysbus_init_irq(sbd, &s->ohci.irq); sysbus_init_mmio(sbd, &s->ohci.mem); } @@ -2026,13 +1895,6 @@ static void usb_ohci_reset_sysbus(DeviceState *dev) ohci_hard_reset(ohci); } -static Property ohci_pci_properties[] = { - 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 const VMStateDescription vmstate_ohci_state_port = { .name = "ohci-core/port", .version_id = 1, @@ -2061,7 +1923,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = { }, }; -static const VMStateDescription vmstate_ohci_state = { +const VMStateDescription vmstate_ohci_state = { .name = "ohci-core", .version_id = 1, .minimum_version_id = 1, @@ -2108,44 +1970,10 @@ static const VMStateDescription vmstate_ohci_state = { } }; -static const VMStateDescription vmstate_ohci = { - .name = "ohci", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState), - VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static void ohci_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = usb_ohci_realize_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; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - dc->desc = "Apple USB Controller"; - dc->props = ohci_pci_properties; - dc->hotpluggable = false; - dc->vmsd = &vmstate_ohci; - dc->reset = usb_ohci_reset_pci; -} - -static const TypeInfo ohci_pci_info = { - .name = TYPE_PCI_OHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(OHCIPCIState), - .class_init = ohci_pci_class_init, -}; - static Property ohci_sysbus_properties[] = { + DEFINE_PROP_STRING("masterbus", OHCISysBusState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), + DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -2157,13 +1985,8 @@ static void ohci_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = ohci_realize_pxa; set_bit(DEVICE_CATEGORY_USB, dc->categories); dc->desc = "OHCI USB Controller"; - dc->props = ohci_sysbus_properties; + device_class_set_props(dc, ohci_sysbus_properties); dc->reset = usb_ohci_reset_sysbus; - /* - * FIXME: Set only because we are not sure yet if this device - * will be outside the q35 sysbus whitelist. - */ - dc->user_creatable = true; } static const TypeInfo ohci_sysbus_info = { @@ -2175,7 +1998,6 @@ static const TypeInfo ohci_sysbus_info = { static void ohci_register_types(void) { - type_register_static(&ohci_pci_info); type_register_static(&ohci_sysbus_info); }