#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
#define MAX_QH 100 // Max allowable queue heads in a chain
#define MIN_FR_PER_TICK 3 // Min frames to process when catching up
+#define PERIODIC_ACTIVE 64
/* Internal periodic / asynchronous schedule state machine states
*/
return 0;
}
+static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
+{
+ EHCIState *s = container_of(bus, EHCIState, bus);
+ uint32_t portsc = s->portsc[ep->dev->port->index];
+
+ if (portsc & PORTSC_POWNER) {
+ return;
+ }
+
+ s->periodic_sched_active = PERIODIC_ACTIVE;
+ qemu_bh_schedule(s->async_bh);
+}
+
static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr)
{
USBDevice *dev;
case USBINTR:
val &= USBINTR_MASK;
+ if (ehci_enabled(s) && (USBSTS_FLR & val)) {
+ qemu_bh_schedule(s->async_bh);
+ }
break;
case FRINDEX:
*mmio, old);
}
-
-// TODO : Put in common header file, duplication from usb-ohci.c
-
/* Get an array of dwords from main memory */
static inline int get_dwords(EHCIState *ehci, uint32_t addr,
uint32_t *buf, int num)
{
int i;
+ if (!ehci->dma) {
+ ehci_raise_irq(ehci, USBSTS_HSE);
+ ehci->usbcmd &= ~USBCMD_RUNSTOP;
+ trace_usb_ehci_dma_error();
+ return -1;
+ }
+
for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
dma_memory_read(ehci->dma, addr, buf, sizeof(*buf));
*buf = le32_to_cpu(*buf);
}
- return 1;
+ return num;
}
/* Put an array of dwords in to main memory */
{
int i;
+ if (!ehci->dma) {
+ ehci_raise_irq(ehci, USBSTS_HSE);
+ ehci->usbcmd &= ~USBCMD_RUNSTOP;
+ trace_usb_ehci_dma_error();
+ return -1;
+ }
+
for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
uint32_t tmp = cpu_to_le32(*buf);
dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp));
}
- return 1;
+ return num;
}
/*
trace_usb_ehci_packet_action(p->queue, p, "wakeup");
p->async = EHCI_ASYNC_FINISHED;
- if (p->queue->async) {
- qemu_bh_schedule(p->queue->ehci->async_bh);
+ if (!p->queue->async) {
+ s->periodic_sched_active = PERIODIC_ACTIVE;
}
+ qemu_bh_schedule(s->async_bh);
}
static void ehci_execute_complete(EHCIQueue *q)
uint32_t i, len, pid, dir, devaddr, endp;
uint32_t pg, off, ptr1, ptr2, max, mult;
+ ehci->periodic_sched_active = PERIODIC_ACTIVE;
+
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
/* Find the head of the list (4.9.1.1) */
for(i = 0; i < MAX_QH; i++) {
- get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
- sizeof(EHCIqh) >> 2);
+ if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
+ sizeof(EHCIqh) >> 2) < 0) {
+ return 0;
+ }
ehci_trace_qh(NULL, NLPTR_GET(entry), &qh);
if (qh.epchar & QH_EPCHAR_H) {
goto out;
}
- get_dwords(ehci, NLPTR_GET(q->qhaddr),
- (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
+ if (get_dwords(ehci, NLPTR_GET(q->qhaddr),
+ (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) {
+ q = NULL;
+ goto out;
+ }
ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh);
/*
assert(!async);
entry = ehci_get_fetch_addr(ehci, async);
- get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
- sizeof(EHCIitd) >> 2);
+ if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
+ sizeof(EHCIitd) >> 2) < 0) {
+ return -1;
+ }
ehci_trace_itd(ehci, entry, &itd);
if (ehci_process_itd(ehci, &itd, entry) != 0) {
assert(!async);
entry = ehci_get_fetch_addr(ehci, async);
- get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd,
- sizeof(EHCIsitd) >> 2);
+ if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd,
+ sizeof(EHCIsitd) >> 2) < 0) {
+ return 0;
+ }
ehci_trace_sitd(ehci, entry, &sitd);
if (!(sitd.results & SITD_RESULTS_ACTIVE)) {
EHCIPacket *p;
int again = 1;
- get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
- sizeof(EHCIqtd) >> 2);
+ if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
+ sizeof(EHCIqtd) >> 2) < 0) {
+ return 0;
+ }
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
p = QTAILQ_FIRST(&q->packets);
goto leave;
}
}
- get_dwords(q->ehci, NLPTR_GET(qtdaddr),
- (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
+ if (get_dwords(q->ehci, NLPTR_GET(qtdaddr),
+ (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) {
+ return -1;
+ }
ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
break;
case EST_WRITEBACK:
assert(q != NULL);
again = ehci_state_writeback(q);
+ if (!async) {
+ ehci->periodic_sched_active = PERIODIC_ACTIVE;
+ }
break;
default:
}
list |= ((ehci->frindex & 0x1ff8) >> 1);
- dma_memory_read(ehci->dma, list, &entry, sizeof entry);
- entry = le32_to_cpu(entry);
+ if (get_dwords(ehci, list, &entry, 1) < 0) {
+ break;
+ }
DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n",
ehci->frindex / 8, list, entry);
if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
need_timer++;
- ehci->async_stepdown = 0;
if (frames > ehci->maxframes) {
skipped_frames = frames - ehci->maxframes;
break;
}
}
+ if (ehci->periodic_sched_active) {
+ ehci->periodic_sched_active--;
+ }
ehci_update_frindex(ehci, 1);
ehci_advance_periodic_state(ehci);
ehci->last_run_ns += FRAME_TIMER_NS;
}
} else {
- if (ehci->async_stepdown < ehci->maxframes / 2) {
- ehci->async_stepdown++;
- }
+ ehci->periodic_sched_active = 0;
ehci_update_frindex(ehci, frames);
ehci->last_run_ns += FRAME_TIMER_NS * frames;
}
+ if (ehci->periodic_sched_active) {
+ ehci->async_stepdown = 0;
+ } else if (ehci->async_stepdown < ehci->maxframes / 2) {
+ ehci->async_stepdown++;
+ }
+
/* Async is not inside loop since it executes everything it can once
* called
*/
ehci->async_stepdown = 0;
}
+ if (ehci_enabled(ehci) && (ehci->usbintr & USBSTS_FLR)) {
+ need_timer++;
+ }
+
if (need_timer) {
/* If we've raised int, we speed up the timer, so that we quickly
* notice any new packets queued up in response */
static USBBusOps ehci_bus_ops = {
.register_companion = ehci_register_companion,
+ .wakeup_endpoint = ehci_wakeup_endpoint,
};
static int usb_ehci_post_load(void *opaque, int version_id)