📄 usb-uhci.c
字号:
} else { /* set connect status */ if (!(port->ctrl & UHCI_PORT_CCS)) { port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; } /* disable port */ if (port->ctrl & UHCI_PORT_EN) { port->ctrl &= ~UHCI_PORT_EN; port->ctrl |= UHCI_PORT_ENC; } dev = port->port.dev; if (dev) { /* send the detach message */ dev->handle_packet(dev, USB_MSG_DETACH, 0, 0, NULL, 0); } port->port.dev = NULL; }}static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, uint8_t devaddr, uint8_t devep, uint8_t *data, int len){ UHCIPort *port; USBDevice *dev; int i, ret;#ifdef DEBUG_PACKET { const char *pidstr; switch(pid) { case USB_TOKEN_SETUP: pidstr = "SETUP"; break; case USB_TOKEN_IN: pidstr = "IN"; break; case USB_TOKEN_OUT: pidstr = "OUT"; break; default: pidstr = "?"; break; } printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n", s->frnum, pidstr, devaddr, devep, len); if (pid != USB_TOKEN_IN) { printf(" data_out="); for(i = 0; i < len; i++) { printf(" %02x", data[i]); } printf("\n"); } }#endif 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, pid, devaddr, devep, data, len); if (ret != USB_RET_NODEV) {#ifdef DEBUG_PACKET { printf(" ret=%d ", ret); if (pid == USB_TOKEN_IN && ret > 0) { printf("data_in="); for(i = 0; i < ret; i++) { printf(" %02x", data[i]); } } printf("\n"); }#endif return ret; } } } return USB_RET_NODEV;}/* 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, int *int_mask){ uint8_t pid; uint8_t buf[1280]; int len, max_len, err, ret; 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; switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: cpu_physical_memory_read(td->buffer, buf, max_len); ret = uhci_broadcast_packet(s, pid, (td->token >> 8) & 0x7f, (td->token >> 15) & 0xf, buf, max_len); len = max_len; break; case USB_TOKEN_IN: ret = uhci_broadcast_packet(s, pid, (td->token >> 8) & 0x7f, (td->token >> 15) & 0xf, buf, max_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, buf, len); } } else { len = 0; } break; default: /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); return -1; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; if (ret >= 0) { td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); td->ctrl &= ~TD_CTRL_ACTIVE; 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_frame_timer(void *opaque){ UHCIState *s = opaque; int64_t expire_time; uint32_t frame_addr, link, old_td_ctrl, val; int int_mask, cnt, ret; UHCI_TD td; UHCI_QH 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; } 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 */ 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 { /* 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); /* 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 == 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); old_td_ctrl = td.ctrl; ret = uhci_handle_td(s, &td, &int_mask); /* 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 */ link = td.link; } } s->frnum = (s->frnum + 1) & 0x7ff; if (int_mask) { s->status2 |= int_mask; s->status |= UHCI_STS_USBINT; uhci_update_irq(s); } /* 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_init(PCIBus *bus, USBPort **usb_ports){ UHCIState *s; uint8_t *pci_conf; UHCIPort *port; int i; s = (UHCIState *)pci_register_device(bus, "USB-UHCI", sizeof(UHCIState), ((PCIDevice *)piix3_state)->devfn + 2, 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++) { port = &s->ports[i]; port->port.opaque = s; port->port.index = i; port->port.attach = uhci_attach; usb_ports[i] = &port->port; } 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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -