switch (addr) { case USBCMD: if (val & USBCMD_HCRESET) { ehci_reset(s); val = s->usbcmd; break; }
/* not supporting dynamic frame list size at the moment */ if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { fprintf(stderr, "attempt to set frame list size -- value %d\n", (int)val & USBCMD_FLS); val &= ~USBCMD_FLS; }
if (val & USBCMD_IAAD) { /* * Process IAAD immediately, otherwise the Linux IAAD watchdog may * trigger and re-use a qh without us seeing the unlink. */ s->async_stepdown = 0; qemu_bh_schedule(s->async_bh); trace_usb_ehci_doorbell_ring(); }
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);//从index为1开始 if ((ehci->frindex & 7) == 0) {//每8位index执行一次周期性传输 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; } .............
/* 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; }
switch(ehci_get_state(ehci, async)) { case EST_INACTIVE: if (!ehci_async_enabled(ehci)) { break; } ehci_set_state(ehci, async, EST_ACTIVE);//如果是INACTIVE则设置为ACTIVE,继续往下执行 // No break, fall through to ACTIVE
case EST_ACTIVE: if (!ehci_async_enabled(ehci)) { ehci_queues_rip_all(ehci, async); ehci_set_state(ehci, async, EST_INACTIVE); break; }
/* make sure guest has acknowledged the doorbell interrupt */ /* TO-DO: is this really needed? */ if (ehci->usbsts & USBSTS_IAA) { DPRINTF("IAA status bit still set.\n"); break; }
/* check that address register has been set */ if (ehci->asynclistaddr == 0) { break; }
/* If the doorbell is set, the guest wants to make a change to the * schedule. The host controller needs to release cached data. * (section 4.8.2) */ if (ehci->usbcmd & USBCMD_IAAD) { /* Remove all unseen qhs from the async qhs queue */ ehci_queues_rip_unseen(ehci, async); trace_usb_ehci_doorbell_ack(); ehci->usbcmd &= ~USBCMD_IAAD; ehci_raise_irq(ehci, USBSTS_IAA); } break;
default: /* this should only be due to a developer mistake */ fprintf(stderr, "ehci: Bad asynchronous state %d. " "Resetting to active\n", ehci->astate); g_assert_not_reached(); } }
case SETUP_STATE_DATA: if (s->setup_buf[0] & USB_DIR_IN) { int len = s->setup_len - s->setup_index; if (len > p->iov.size) {//len最大为0xffff,但是setup_index可以任意构造 len = p->iov.size; } usb_packet_copy(p, s->data_buf + s->setup_index, len);//读数据 //.......... }
case SETUP_STATE_DATA: if (!(s->setup_buf[0] & USB_DIR_IN)) { int len = s->setup_len - s->setup_index; if (len > p->iov.size) { len = p->iov.size; } usb_packet_copy(p, s->data_buf + s->setup_index, len);//同上
QLIST_HEAD(, USBDescString) strings; const USBDesc *usb_desc; /* Overrides class usb_desc if not NULL */ const USBDescDevice *device;
int configuration; int ninterfaces; int altsetting[USB_MAX_INTERFACES]; const USBDescConfig *config; const USBDescIface *ifaces[USB_MAX_INTERFACES]; };
structUSBPort { USBDevice *dev; int speedmask; int hubcount; char path[16]; USBPortOps *ops;//important void *opaque; int index; /* internal port index, may be used with the opaque */ QTAILQ_ENTRY(USBPort) next; };
typedefstructUSBPortOps {//有着大量的函数指针,可以拿来泄露 void (*attach)(USBPort *port); void (*detach)(USBPort *port); /* * This gets called when a device downstream from the device attached to * the port (iow attached through a hub) gets detached. */ void (*child_detach)(USBPort *port, USBDevice *child); void (*wakeup)(USBPort *port); /* * Note that port->dev will be different then the device from which * the packet originated when a hub is involved. */ void (*complete)(USBPort *port, USBPacket *p); } USBPortOps;
if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) {//最大可达BUFF_SIZE=5*4096 ehci_trace_guest_bug(p->queue->ehci, "guest requested more bytes than allowed"); return-1; }
//......... if (p->async == EHCI_ASYNC_NONE) { if (ehci_init_transfer(p) != 0) {//初始化传输空间p->sgl return-1; }