📄 usb-ohci.c
字号:
/* * QEMU USB OHCI Emulation * Copyright (c) 2004 Gianni Tedesco * Copyright (c) 2006 CodeSourcery * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * TODO: * o Isochronous transfers * o Allocate bandwidth in frames properly * o Disable timers when nothing needs to be done, or remove timer usage * all together. * o Handle unrecoverable errors properly * o BIOS work to boot from USB storage*/#include "vl.h"//#define DEBUG_OHCI/* Dump packet contents. *///#define DEBUG_PACKET/* This causes frames to occur 1000x slower *///#define OHCI_TIME_WARP 1#ifdef DEBUG_OHCI#define dprintf printf#else#define dprintf(...)#endif/* Number of Downstream Ports on the root hub. */#define OHCI_MAX_PORTS 15static int64_t usb_frame_time;static int64_t usb_bit_time;typedef struct OHCIPort { USBPort port; uint32_t ctrl;} OHCIPort;typedef struct { struct PCIDevice pci_dev; target_phys_addr_t mem_base; int mem; int num_ports; QEMUTimer *eof_timer; int64_t sof_time; /* OHCI state */ /* Control partition */ uint32_t ctl, status; uint32_t intr_status; uint32_t intr; /* memory pointer partition */ uint32_t hcca; uint32_t ctrl_head, ctrl_cur; uint32_t bulk_head, bulk_cur; uint32_t per_cur; uint32_t done; int done_count; /* Frame counter partition */ uint32_t fsmps:15; uint32_t fit:1; uint32_t fi:14; uint32_t frt:1; uint16_t frame_number; uint16_t padding; uint32_t pstart; uint32_t lst; /* Root Hub partition */ uint32_t rhdesc_a, rhdesc_b; uint32_t rhstatus; OHCIPort rhport[OHCI_MAX_PORTS]; /* Active packets. */ uint32_t old_ctl; USBPacket usb_packet; uint8_t usb_buf[8192]; uint32_t async_td; int async_complete;} OHCIState;/* Host Controller Communications Area */struct ohci_hcca { uint32_t intr[32]; uint16_t frame, pad; uint32_t done;};/* Bitfields for the first word of an Endpoint Desciptor. */#define OHCI_ED_FA_SHIFT 0#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)#define OHCI_ED_EN_SHIFT 7#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT)#define OHCI_ED_D_SHIFT 11#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT)#define OHCI_ED_S (1<<13)#define OHCI_ED_K (1<<14)#define OHCI_ED_F (1<<15)#define OHCI_ED_MPS_SHIFT 7#define OHCI_ED_MPS_MASK (0xf<<OHCI_ED_FA_SHIFT)/* Flags in the head field of an Endpoint Desciptor. */#define OHCI_ED_H 1#define OHCI_ED_C 2/* Bitfields for the first word of a Transfer Desciptor. */#define OHCI_TD_R (1<<18)#define OHCI_TD_DP_SHIFT 19#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT)#define OHCI_TD_DI_SHIFT 21#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)#define OHCI_TD_T0 (1<<24)#define OHCI_TD_T1 (1<<24)#define OHCI_TD_EC_SHIFT 26#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)#define OHCI_TD_CC_SHIFT 28#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT)#define OHCI_DPTR_MASK 0xfffffff0#define OHCI_BM(val, field) \ (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)#define OHCI_SET_BM(val, field, newval) do { \ val &= ~OHCI_##field##_MASK; \ val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \ } while(0)/* endpoint descriptor */struct ohci_ed { uint32_t flags; uint32_t tail; uint32_t head; uint32_t next;};/* General transfer descriptor */struct ohci_td { uint32_t flags; uint32_t cbp; uint32_t next; uint32_t be;};#define USB_HZ 12000000/* OHCI Local stuff */#define OHCI_CTL_CBSR ((1<<0)|(1<<1))#define OHCI_CTL_PLE (1<<2)#define OHCI_CTL_IE (1<<3)#define OHCI_CTL_CLE (1<<4)#define OHCI_CTL_BLE (1<<5)#define OHCI_CTL_HCFS ((1<<6)|(1<<7))#define OHCI_USB_RESET 0x00#define OHCI_USB_RESUME 0x40#define OHCI_USB_OPERATIONAL 0x80#define OHCI_USB_SUSPEND 0xc0#define OHCI_CTL_IR (1<<8)#define OHCI_CTL_RWC (1<<9)#define OHCI_CTL_RWE (1<<10)#define OHCI_STATUS_HCR (1<<0)#define OHCI_STATUS_CLF (1<<1)#define OHCI_STATUS_BLF (1<<2)#define OHCI_STATUS_OCR (1<<3)#define OHCI_STATUS_SOC ((1<<6)|(1<<7))#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */#define OHCI_INTR_WD (1<<1) /* HcDoneHead writeback */#define OHCI_INTR_SF (1<<2) /* Start of frame */#define OHCI_INTR_RD (1<<3) /* Resume detect */#define OHCI_INTR_UE (1<<4) /* Unrecoverable error */#define OHCI_INTR_FNO (1<<5) /* Frame number overflow */#define OHCI_INTR_RHSC (1<<6) /* Root hub status change */#define OHCI_INTR_OC (1<<30) /* Ownership change */#define OHCI_INTR_MIE (1<<31) /* Master Interrupt Enable */#define OHCI_HCCA_SIZE 0x100#define OHCI_HCCA_MASK 0xffffff00#define OHCI_EDPTR_MASK 0xfffffff0#define OHCI_FMI_FI 0x00003fff#define OHCI_FMI_FSMPS 0xffff0000#define OHCI_FMI_FIT 0x80000000#define OHCI_FR_RT (1<<31)#define OHCI_LS_THRESH 0x628#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */#define OHCI_RHA_PSM (1<<8)#define OHCI_RHA_NPS (1<<9)#define OHCI_RHA_DT (1<<10)#define OHCI_RHA_OCPM (1<<11)#define OHCI_RHA_NOCP (1<<12)#define OHCI_RHA_POTPGT_MASK 0xff000000#define OHCI_RHS_LPS (1<<0)#define OHCI_RHS_OCI (1<<1)#define OHCI_RHS_DRWE (1<<15)#define OHCI_RHS_LPSC (1<<16)#define OHCI_RHS_OCIC (1<<17)#define OHCI_RHS_CRWE (1<<31)#define OHCI_PORT_CCS (1<<0)#define OHCI_PORT_PES (1<<1)#define OHCI_PORT_PSS (1<<2)#define OHCI_PORT_POCI (1<<3)#define OHCI_PORT_PRS (1<<4)#define OHCI_PORT_PPS (1<<8)#define OHCI_PORT_LSDA (1<<9)#define OHCI_PORT_CSC (1<<16)#define OHCI_PORT_PESC (1<<17)#define OHCI_PORT_PSSC (1<<18)#define OHCI_PORT_OCIC (1<<19)#define OHCI_PORT_PRSC (1<<20)#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \ |OHCI_PORT_OCIC|OHCI_PORT_PRSC)#define OHCI_TD_DIR_SETUP 0x0#define OHCI_TD_DIR_OUT 0x1#define OHCI_TD_DIR_IN 0x2#define OHCI_TD_DIR_RESERVED 0x3#define OHCI_CC_NOERROR 0x0#define OHCI_CC_CRC 0x1#define OHCI_CC_BITSTUFFING 0x2#define OHCI_CC_DATATOGGLEMISMATCH 0x3#define OHCI_CC_STALL 0x4#define OHCI_CC_DEVICENOTRESPONDING 0x5#define OHCI_CC_PIDCHECKFAILURE 0x6#define OHCI_CC_UNDEXPETEDPID 0x7#define OHCI_CC_DATAOVERRUN 0x8#define OHCI_CC_DATAUNDERRUN 0x9#define OHCI_CC_BUFFEROVERRUN 0xc#define OHCI_CC_BUFFERUNDERRUN 0xd/* Update IRQ levels */static inline void ohci_intr_update(OHCIState *ohci){ int level = 0; if ((ohci->intr & OHCI_INTR_MIE) && (ohci->intr_status & ohci->intr)) level = 1; pci_set_irq(&ohci->pci_dev, 0, level);}/* Set an interrupt */static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr){ ohci->intr_status |= intr; ohci_intr_update(ohci);}/* Attach or detach a device on a root hub port. */static void ohci_attach(USBPort *port1, USBDevice *dev){ OHCIState *s = port1->opaque; OHCIPort *port = &s->rhport[port1->index]; uint32_t old_state = port->ctrl; if (dev) { if (port->port.dev) { usb_attach(port1, NULL); } /* set connect status */ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; /* update speed */ if (dev->speed == USB_SPEED_LOW) port->ctrl |= OHCI_PORT_LSDA; else port->ctrl &= ~OHCI_PORT_LSDA; port->port.dev = dev; /* send the attach message */ usb_send_msg(dev, USB_MSG_ATTACH); dprintf("usb-ohci: Attached port %d\n", port1->index); } else { /* set connect status */ if (port->ctrl & OHCI_PORT_CCS) { port->ctrl &= ~OHCI_PORT_CCS; port->ctrl |= OHCI_PORT_CSC; } /* disable port */ if (port->ctrl & OHCI_PORT_PES) { port->ctrl &= ~OHCI_PORT_PES; port->ctrl |= OHCI_PORT_PESC; } dev = port->port.dev; if (dev) { /* send the detach message */ usb_send_msg(dev, USB_MSG_DETACH); } port->port.dev = NULL; dprintf("usb-ohci: Detached port %d\n", port1->index); } if (old_state != port->ctrl) ohci_set_interrupt(s, OHCI_INTR_RHSC);}/* Reset the controller */static void ohci_reset(OHCIState *ohci){ OHCIPort *port; int i; ohci->ctl = 0; ohci->old_ctl = 0; ohci->status = 0; ohci->intr_status = 0; ohci->intr = OHCI_INTR_MIE; ohci->hcca = 0; ohci->ctrl_head = ohci->ctrl_cur = 0; ohci->bulk_head = ohci->bulk_cur = 0; ohci->per_cur = 0; ohci->done = 0; ohci->done_count = 7; /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? * I took the value linux sets ... */ ohci->fsmps = 0x2778; ohci->fi = 0x2edf; ohci->fit = 0; ohci->frt = 0; ohci->frame_number = 0; ohci->pstart = 0; ohci->lst = OHCI_LS_THRESH; ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; ohci->rhdesc_b = 0x0; /* Impl. specific */ ohci->rhstatus = 0; for (i = 0; i < ohci->num_ports; i++) { port = &ohci->rhport[i]; port->ctrl = 0; if (port->port.dev) ohci_attach(&port->port, port->port.dev); } if (ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name);}/* Get an array of dwords from main memory */static inline int get_dwords(uint32_t addr, uint32_t *buf, int num){ int i; for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); *buf = le32_to_cpu(*buf); } return 1;}/* Put an array of dwords in to main memory */static inline int put_dwords(uint32_t addr, uint32_t *buf, int num){ int i; for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); } return 1;}static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed){ return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);}static inline int ohci_read_td(uint32_t addr, struct ohci_td *td){ return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);}static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed){ return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);}static inline int ohci_put_td(uint32_t addr, struct ohci_td *td){ return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);}/* Read/Write the contents of a TD from/to main memory. */static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write){ uint32_t ptr; uint32_t n;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -