?? usb-uhci.c.svn-base
字號(hào):
for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; dev = port->port.dev; if (dev && (port->ctrl & UHCI_PORT_EN)) { ret = dev->handle_packet(dev, p); if (ret != USB_RET_NODEV) {#ifdef DEBUG_PACKET if (ret == USB_RET_ASYNC) { printf("usb-uhci: Async packet\n"); } else { printf(" ret=%d ", ret); if (p->pid == USB_TOKEN_IN && ret > 0) { printf("data_in="); for(i = 0; i < ret; i++) { printf(" %02x", p->data[i]); } } printf("\n"); }#endif return ret; } } } return USB_RET_NODEV;}static void uhci_async_complete_packet(USBPacket * packet, void *opaque);/* return -1 if fatal error (frame must be stopped) 0 if TD successful 1 if TD unsuccessful or inactive*/static int uhci_handle_td(UHCIState *s, UHCI_TD *td, uint32_t *int_mask, int completion){ uint8_t pid; int len = 0, max_len, err, ret = 0; /* ??? This is wrong for async completion. */ if (td->ctrl & TD_CTRL_IOC) { *int_mask |= 0x01; } if (!(td->ctrl & TD_CTRL_ACTIVE)) return 1; /* TD is active */ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; if (completion && (s->async_qh || s->async_frame_addr)) { ret = s->usb_packet.len; if (ret >= 0) { len = ret; if (len > max_len) { len = max_len; ret = USB_RET_BABBLE; } if (len > 0) { /* write the data back */ cpu_physical_memory_write(td->buffer, s->usb_buf, len); } } else { len = 0; } s->async_qh = 0; s->async_frame_addr = 0; } else if (!completion) { s->usb_packet.pid = pid; s->usb_packet.devaddr = (td->token >> 8) & 0x7f; s->usb_packet.devep = (td->token >> 15) & 0xf; s->usb_packet.data = s->usb_buf; s->usb_packet.len = max_len; s->usb_packet.complete_cb = uhci_async_complete_packet; s->usb_packet.complete_opaque = s; switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: cpu_physical_memory_read(td->buffer, s->usb_buf, max_len); ret = uhci_broadcast_packet(s, &s->usb_packet); len = max_len; break; case USB_TOKEN_IN: ret = uhci_broadcast_packet(s, &s->usb_packet); if (ret >= 0) { len = ret; if (len > max_len) { len = max_len; ret = USB_RET_BABBLE; } if (len > 0) { /* write the data back */ cpu_physical_memory_write(td->buffer, s->usb_buf, len); } } else { len = 0; } break; default: /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); return -1; } } if (ret == USB_RET_ASYNC) { return 2; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; if (ret >= 0) { td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); /* The NAK bit may have been set by a previous frame, so clear it here. The docs are somewhat unclear, but win2k relies on this behavior. */ td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); if (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ return 1; } else { /* success */ return 0; } } else { switch(ret) { default: case USB_RET_NODEV: do_timeout: td->ctrl |= TD_CTRL_TIMEOUT; err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3; if (err != 0) { err--; if (err == 0) { td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; uhci_update_irq(s); } } td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); return 1; case USB_RET_NAK: td->ctrl |= TD_CTRL_NAK; if (pid == USB_TOKEN_SETUP) goto do_timeout; return 1; case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; return 1; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; /* frame interrupted */ return -1; } }}static void uhci_async_complete_packet(USBPacket * packet, void *opaque){ UHCIState *s = opaque; UHCI_QH qh; UHCI_TD td; uint32_t link; uint32_t old_td_ctrl; uint32_t val; uint32_t frame_addr; int ret; /* Handle async isochronous packet completion */ frame_addr = s->async_frame_addr; if (frame_addr) { cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); le32_to_cpus(&link); cpu_physical_memory_read(link & ~0xf, (uint8_t *)&td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); old_td_ctrl = td.ctrl; ret = uhci_handle_td(s, &td, &s->pending_int_mask, 1); /* update the status bits of the TD */ if (old_td_ctrl != td.ctrl) { val = cpu_to_le32(td.ctrl); cpu_physical_memory_write((link & ~0xf) + 4, (const uint8_t *)&val, sizeof(val)); } if (ret == 2) { s->async_frame_addr = frame_addr; } else if (ret == 0) { /* update qh element link */ val = cpu_to_le32(td.link); cpu_physical_memory_write(frame_addr, (const uint8_t *)&val, sizeof(val)); } return; } link = s->async_qh; if (!link) { /* This should never happen. It means a TD somehow got removed without cancelling the associated async IO request. */ return; } cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); /* Re-process the queue containing the async packet. */ while (1) { cpu_physical_memory_read(qh.el_link & ~0xf, (uint8_t *)&td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); old_td_ctrl = td.ctrl; ret = uhci_handle_td(s, &td, &s->pending_int_mask, 1); /* update the status bits of the TD */ if (old_td_ctrl != td.ctrl) { val = cpu_to_le32(td.ctrl); cpu_physical_memory_write((qh.el_link & ~0xf) + 4, (const uint8_t *)&val, sizeof(val)); } if (ret < 0) break; /* interrupted frame */ if (ret == 2) { s->async_qh = link; break; } else if (ret == 0) { /* update qh element link */ qh.el_link = td.link; val = cpu_to_le32(qh.el_link); cpu_physical_memory_write((link & ~0xf) + 4, (const uint8_t *)&val, sizeof(val)); if (!(qh.el_link & 4)) break; } break; }}static void uhci_frame_timer(void *opaque){ UHCIState *s = opaque; int64_t expire_time; uint32_t frame_addr, link, old_td_ctrl, val, int_mask; int cnt, ret; UHCI_TD td; UHCI_QH qh; uint32_t old_async_qh; if (!(s->cmd & UHCI_CMD_RS)) { qemu_del_timer(s->frame_timer); /* set hchalted bit in status - UHCI11D 2.1.2 */ s->status |= UHCI_STS_HCHALTED; return; } /* Complete the previous frame. */ s->frnum = (s->frnum + 1) & 0x7ff; if (s->pending_int_mask) { s->status2 |= s->pending_int_mask; s->status |= UHCI_STS_USBINT; uhci_update_irq(s); } old_async_qh = s->async_qh; frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); le32_to_cpus(&link); int_mask = 0; cnt = FRAME_MAX_LOOPS; while ((link & 1) == 0) { if (--cnt == 0) break; /* valid frame */ if (link & 2) { /* QH */ if (link == s->async_qh) { /* We've found a previously issues packet. Nothing else to do. */ old_async_qh = 0; break; } cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); depth_first: if (qh.el_link & 1) { /* no element : go to next entry */ link = qh.link; } else if (qh.el_link & 2) { /* QH */ link = qh.el_link; } else if (s->async_qh) { /* We can only cope with one pending packet. Keep looking for the previously issued packet. */ link = qh.link; } else { /* TD */ if (--cnt == 0) break; cpu_physical_memory_read(qh.el_link & ~0xf, (uint8_t *)&td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); old_td_ctrl = td.ctrl; ret = uhci_handle_td(s, &td, &int_mask, 0); /* update the status bits of the TD */ if (old_td_ctrl != td.ctrl) { val = cpu_to_le32(td.ctrl); cpu_physical_memory_write((qh.el_link & ~0xf) + 4, (const uint8_t *)&val, sizeof(val)); } if (ret < 0) break; /* interrupted frame */ if (ret == 2) { s->async_qh = link; } else if (ret == 0) { /* update qh element link */ qh.el_link = td.link; val = cpu_to_le32(qh.el_link); cpu_physical_memory_write((link & ~0xf) + 4, (const uint8_t *)&val, sizeof(val)); if (qh.el_link & 4) { /* depth first */ goto depth_first; } } /* go to next entry */ link = qh.link; } } else { /* TD */ cpu_physical_memory_read(link & ~0xf, (uint8_t *)&td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); /* Handle isochonous transfer. */ /* FIXME: might be more than one isoc in frame */ old_td_ctrl = td.ctrl; ret = uhci_handle_td(s, &td, &int_mask, 0); /* update the status bits of the TD */ if (old_td_ctrl != td.ctrl) { val = cpu_to_le32(td.ctrl); cpu_physical_memory_write((link & ~0xf) + 4, (const uint8_t *)&val, sizeof(val)); } if (ret < 0) break; /* interrupted frame */ if (ret == 2) { s->async_frame_addr = frame_addr; } link = td.link; } } s->pending_int_mask = int_mask; if (old_async_qh) { /* A previously started transfer has disappeared from the transfer list. There's nothing useful we can do with it now, so just discard the packet and hope it wasn't too important. */#ifdef DEBUG printf("Discarding USB packet\n");#endif usb_cancel_packet(&s->usb_packet); s->async_qh = 0; } /* prepare the timer for the next frame */ expire_time = qemu_get_clock(vm_clock) + (ticks_per_sec / FRAME_TIMER_FREQ); qemu_mod_timer(s->frame_timer, expire_time);}static void uhci_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type){ UHCIState *s = (UHCIState *)pci_dev; register_ioport_write(addr, 32, 2, uhci_ioport_writew, s); register_ioport_read(addr, 32, 2, uhci_ioport_readw, s); register_ioport_write(addr, 32, 4, uhci_ioport_writel, s); register_ioport_read(addr, 32, 4, uhci_ioport_readl, s); register_ioport_write(addr, 32, 1, uhci_ioport_writeb, s); register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);}void usb_uhci_piix3_init(PCIBus *bus, int devfn){ UHCIState *s; uint8_t *pci_conf; int i; s = (UHCIState *)pci_register_device(bus, "USB-UHCI", sizeof(UHCIState), devfn, NULL, NULL); pci_conf = s->dev.config; pci_conf[0x00] = 0x86; pci_conf[0x01] = 0x80; pci_conf[0x02] = 0x20; pci_conf[0x03] = 0x70; pci_conf[0x08] = 0x01; // revision number pci_conf[0x09] = 0x00; pci_conf[0x0a] = 0x03; pci_conf[0x0b] = 0x0c; pci_conf[0x0e] = 0x00; // header_type pci_conf[0x3d] = 4; // interrupt pin 3 pci_conf[0x60] = 0x10; // release number for(i = 0; i < NB_PORTS; i++) { qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); uhci_reset(s); /* Use region 4 for consistency with real hardware. BSD guests seem to rely on this. */ pci_register_io_region(&s->dev, 4, 0x20, PCI_ADDRESS_SPACE_IO, uhci_map);}void usb_uhci_piix4_init(PCIBus *bus, int devfn){ UHCIState *s; uint8_t *pci_conf; int i; s = (UHCIState *)pci_register_device(bus, "USB-UHCI", sizeof(UHCIState), devfn, NULL, NULL); pci_conf = s->dev.config; pci_conf[0x00] = 0x86; pci_conf[0x01] = 0x80; pci_conf[0x02] = 0x12; pci_conf[0x03] = 0x71; pci_conf[0x08] = 0x01; // revision number pci_conf[0x09] = 0x00; pci_conf[0x0a] = 0x03; pci_conf[0x0b] = 0x0c; pci_conf[0x0e] = 0x00; // header_type pci_conf[0x3d] = 4; // interrupt pin 3 pci_conf[0x60] = 0x10; // release number for(i = 0; i < NB_PORTS; i++) { qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); uhci_reset(s); /* Use region 4 for consistency with real hardware. BSD guests seem to rely on this. */ pci_register_io_region(&s->dev, 4, 0x20, PCI_ADDRESS_SPACE_IO, uhci_map);}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -