* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/usb/ehci-regs.h"
#include "hw/usb/hcd-ehci.h"
#include "trace.h"
#define FRAME_TIMER_FREQ 1000
-#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ)
+#define FRAME_TIMER_NS (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ)
#define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8)
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
ehci_queues_rip_device(s, port->dev, 0);
ehci_queues_rip_device(s, port->dev, 1);
- *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
+ *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND);
*portsc |= PORTSC_CSC;
ehci_raise_irq(s, USBSTS_PCD);
qemu_bh_schedule(s->async_bh);
}
-static int ehci_register_companion(USBBus *bus, USBPort *ports[],
- uint32_t portcount, uint32_t firstport)
+static void ehci_register_companion(USBBus *bus, USBPort *ports[],
+ uint32_t portcount, uint32_t firstport,
+ Error **errp)
{
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;
+ error_setg(errp, "firstport must be between 0 and %u",
+ NB_PORTS - portcount);
+ return;
}
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;
+ error_setg(errp, "firstport %u asks for ports %u-%u,"
+ " but port %u has a companion assigned already",
+ firstport, firstport, firstport + portcount - 1,
+ firstport + i);
+ return;
}
}
s->companion_count++;
s->caps[0x05] = (s->companion_count << 4) | portcount;
-
- return 0;
}
static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
}
/* 4.1 host controller initialization */
-static void ehci_reset(void *opaque)
+void ehci_reset(void *opaque)
{
EHCIState *s = opaque;
int i;
s->usbsts = USBSTS_HALT;
s->usbsts_pending = 0;
s->usbsts_frindex = 0;
+ ehci_update_irq(s);
s->astate = EST_INACTIVE;
s->pstate = EST_INACTIVE;
return s->caps[addr];
}
+static void ehci_caps_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+}
+
static uint64_t ehci_opreg_read(void *ptr, hwaddr addr,
unsigned size)
{
{
USBDevice *dev;
USBEndpoint *ep;
- uint32_t i, len, pid, dir, devaddr, endp;
+ uint32_t i, len, pid, dir, devaddr, endp, xfers = 0;
uint32_t pg, off, ptr1, ptr2, max, mult;
ehci->periodic_sched_active = PERIODIC_ACTIVE;
if (itd->transact[i] & ITD_XACT_ACTIVE) {
pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
off = itd->transact[i] & ITD_XACT_OFFSET_MASK;
- ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
- ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK);
len = get_field(itd->transact[i], ITD_XACT_LENGTH);
if (len > max * mult) {
len = max * mult;
}
-
- if (len > BUFF_SIZE) {
+ if (len > BUFF_SIZE || pg > 6) {
return -1;
}
+ ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as);
if (off + len > 4096) {
/* transfer crosses page border */
+ if (pg == 6) {
+ return -1; /* avoid page pg + 1 */
+ }
+ ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK);
uint32_t len2 = off + len - 4096;
uint32_t len1 = len - len2;
qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
ehci_raise_irq(ehci, USBSTS_INT);
}
itd->transact[i] &= ~ITD_XACT_ACTIVE;
+ xfers++;
}
}
- return 0;
+ return xfers ? 0 : -1;
}
entry = ehci_get_fetch_addr(ehci, async);
q = ehci_find_queue_by_qh(ehci, entry, async);
- if (NULL == q) {
+ if (q == NULL) {
q = ehci_alloc_queue(ehci, entry, async);
}
static const MemoryRegionOps ehci_mmio_caps_ops = {
.read = ehci_caps_read,
+ .write = ehci_caps_write,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.impl.min_access_size = 1,
.complete = ehci_async_complete_packet,
};
-static USBBusOps ehci_bus_ops = {
+static USBBusOps ehci_bus_ops_companion = {
.register_companion = ehci_register_companion,
.wakeup_endpoint = ehci_wakeup_endpoint,
};
+static USBBusOps ehci_bus_ops_standalone = {
+ .wakeup_endpoint = ehci_wakeup_endpoint,
+};
static void usb_ehci_pre_save(void *opaque)
{
VMSTATE_UINT32(portsc[4], EHCIState),
VMSTATE_UINT32(portsc[5], EHCIState),
/* frame timer */
- VMSTATE_TIMER(frame_timer, EHCIState),
+ VMSTATE_TIMER_PTR(frame_timer, EHCIState),
VMSTATE_UINT64(last_run_ns, EHCIState),
VMSTATE_UINT32(async_stepdown, EHCIState),
/* schedule state */
return;
}
- usb_bus_new(&s->bus, sizeof(s->bus), &ehci_bus_ops, dev);
+ usb_bus_new(&s->bus, sizeof(s->bus), s->companion_enable ?
+ &ehci_bus_ops_companion : &ehci_bus_ops_standalone, dev);
for (i = 0; i < s->portnr; i++) {
usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
USB_SPEED_MASK_HIGH);
s->async_bh = qemu_bh_new(ehci_frame_timer, s);
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)