static void ehci_frame_timer(void *opaque) { EHCIState *ehci = opaque; int need_timer = 0; int64_t expire_time, t_now; uint64_t ns_elapsed; uint64_t uframes, skipped_uframes; int i; t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ns_elapsed = t_now - ehci->last_run_ns; uframes = ns_elapsed / UFRAME_TIMER_NS; if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) { need_timer++; if (uframes > (ehci->maxframes * 8)) { skipped_uframes = uframes - (ehci->maxframes * 8); ehci_update_frindex(ehci, skipped_uframes); ehci->last_run_ns += UFRAME_TIMER_NS * skipped_uframes; uframes -= skipped_uframes; DPRINTF("WARNING - EHCI skipped %d uframes\n", skipped_uframes); } for (i = 0; i < uframes; i++) { /* * If we're running behind schedule, we should not catch up * too fast, as that will make some guests unhappy: * 1) We must process a minimum of MIN_UFR_PER_TICK frames, * otherwise we will never catch up * 2) Process frames until the guest has requested an irq (IOC) */ if (i >= MIN_UFR_PER_TICK) { ehci_commit_irq(ehci); if ((ehci->usbsts & USBINTR_MASK) & ehci->usbintr) { break; } } if (ehci->periodic_sched_active) { ehci->periodic_sched_active--; } ehci_update_frindex(ehci, 1); if ((ehci->frindex & 7) == 0) { ehci_advance_periodic_state(ehci); } ehci->last_run_ns += UFRAME_TIMER_NS; } } else { ehci->periodic_sched_active = 0; ehci_update_frindex(ehci, uframes); ehci->last_run_ns += UFRAME_TIMER_NS * uframes; } 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 */ if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) { need_timer++; ehci_advance_async_state(ehci); } ehci_commit_irq(ehci); if (ehci->usbsts_pending) { need_timer++; 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 */ if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) { expire_time = t_now + NANOSECONDS_PER_SECOND / (FRAME_TIMER_FREQ * 4); ehci->int_req_by_async = false; } else { expire_time = t_now + (NANOSECONDS_PER_SECOND * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ); } timer_mod(ehci->frame_timer, expire_time); } }