diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ohci.c b/reactos/drivers/usb/nt4compat/usbdriver/ohci.c index 6d586c37bb1..c65b59d190b 100644 --- a/reactos/drivers/usb/nt4compat/usbdriver/ohci.c +++ b/reactos/drivers/usb/nt4compat/usbdriver/ohci.c @@ -87,6 +87,12 @@ static u32 roothub_portstatus (struct ohci_hcd *hc, int i) */ +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR +#define OHCI_INTR_INIT \ + (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH) + + VOID ohci_wait_ms(POHCI_DEV ohci, LONG ms) { @@ -161,6 +167,28 @@ PDEVICE_OBJECT ohci_probe(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, return NULL; } +BOOLEAN ohci_mem_init (POHCI_DEVICE_EXTENSION dev_ext) +{ + dev_ext->ohci->td_cache = HalAllocateCommonBuffer(dev_ext->padapter, + sizeof(OHCI_TD), &dev_ext->ohci->td_logic_addr, FALSE); + + if (!dev_ext->ohci->td_cache) + return FALSE; + + dev_ext->ohci->ed_cache = HalAllocateCommonBuffer(dev_ext->padapter, + sizeof(OHCI_ED), &dev_ext->ohci->ed_logic_addr, FALSE); + + if (!dev_ext->ohci->ed_cache) + { + HalFreeCommonBuffer(dev_ext->padapter, sizeof(OHCI_TD), dev_ext->ohci->td_logic_addr, + dev_ext->ohci->td_cache, FALSE); + return FALSE; + } + + return TRUE; +} + + PDEVICE_OBJECT ohci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PUSB_DEV_MANAGER dev_mgr) { @@ -201,7 +229,9 @@ ohci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PU dev_desc.Dma32BitAddresses = TRUE; dev_desc.BusNumber = bus; dev_desc.InterfaceType = PCIBus; - //dev_desc.MaximumLength = EHCI_MAX_SIZE_TRANSFER; + dev_desc.MaximumLength = EHCI_MAX_SIZE_TRANSFER; + pdev_ext->map_regs = 2; // we do not use it seriously + pdev_ext->padapter = HalGetAdapter(&dev_desc, &pdev_ext->map_regs); DbgPrint("ohci_alloc(): reg_path=0x%x, \n \ ohci_alloc(): bus=0x%x, bus_addr=0x%x \n \ @@ -333,13 +363,20 @@ ohci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PU DbgPrint("OHCI: %d ports\n", pdev_ext->ohci->num_ports); - //ohci->hcca = dma_alloc_coherent (ohci_to_hcd(ohci)->self.controller, - // sizeof *ohci->hcca, &ohci->hcca_dma, 0); - //if (!ohci->hcca) - // return -ENOMEM; + pdev_ext->ohci->hcca = HalAllocateCommonBuffer(pdev_ext->padapter, + sizeof(*pdev_ext->ohci->hcca), &pdev_ext->ohci->hcca_logic_addr, FALSE); - //if ((ret = ohci_mem_init (ohci)) < 0) - // ohci_stop (ohci_to_hcd(ohci)); + if (!pdev_ext->ohci->hcca) + { + DbgPrint("OHCI: HCCA allocation failed!\n"); + return NULL; + } + + if (!ohci_mem_init(pdev_ext)) + { + DbgPrint("OHCI: Mem init failed!\n"); + return NULL; + } #if 0 //init ehci_caps @@ -464,72 +501,137 @@ ohci_create_device(PDRIVER_OBJECT drvr_obj, PUSB_DEV_MANAGER dev_mgr) BOOLEAN ohci_start(PHCD hcd) { - //ULONG tmp; + ULONG temp, mask; //PBYTE base; //PEHCI_USBCMD_CONTENT usbcmd; - //POHCI_DEV ohci; + POHCI_DEV ohci; + ULONG hc_control; if (hcd == NULL) return FALSE; - return TRUE; + ohci = struct_ptr(hcd, OHCI_DEV, hcd_interf); + + /* Reset USB nearly "by the book". RemoteWakeupConnected + * saved if boot firmware (BIOS/SMM/...) told us it's connected + * (for OHCI integrated on mainboard, it normally is) + */ + hc_control = OHCI_READ_PORT_ULONG((PULONG)&ohci->regs->control); + DbgPrint("resetting from state %x, control = 0x%x\n", + (hc_control & OHCI_CTRL_HCFS), + hc_control); + + //if (hc_control & OHCI_CTRL_RWC + // && !(ohci->flags & OHCI_QUIRK_AMD756)) + // ohci_to_hcd(ohci)->can_wakeup = 1; + + switch (hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_OPER: + temp = 0; + break; + case OHCI_USB_SUSPEND: + case OHCI_USB_RESUME: + hc_control &= OHCI_CTRL_RWC; + hc_control |= OHCI_USB_RESUME; + temp = 10 /* msec wait */; + break; + // case OHCI_USB_RESET: + default: + hc_control &= OHCI_CTRL_RWC; + hc_control |= OHCI_USB_RESET; + temp = 50 /* msec wait */; + break; + } + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->control, hc_control); + + // flush the writes + (VOID)OHCI_READ_PORT_ULONG((PULONG)&ohci->regs->control); + + ohci_wait_ms(ohci, temp); + temp = roothub_a (ohci); + if (!(temp & RH_A_NPS)) { + /* power down each port */ + for (temp = 0; temp < ohci->num_ports; temp++) + { + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->roothub.portstatus [temp], RH_PS_LSDA); + } + } + // flush those writes + (VOID)OHCI_READ_PORT_ULONG((PULONG)&ohci->regs->control); + RtlZeroMemory(ohci->hcca, sizeof(OHCI_HCCA)); + + /* 2msec timelimit here means no irqs/preempt */ + //spin_lock_irq (&ohci->lock); + +//retry: + /* HC Reset requires max 10 us delay */ + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->cmdstatus, OHCI_HCR); + temp = 30; /* ... allow extra time */ + while ((OHCI_READ_PORT_ULONG ((PULONG)&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--temp == 0) { + //spin_unlock_irq (&ohci->lock); + //ohci_err (ohci, "USB HC reset timed out!\n"); + DbgPrint("OHCI: USB HC reset timed out!\n"); + return FALSE; + } + KeStallExecutionProcessor(1); + } + + /* now we're in the SUSPEND state ... must go OPERATIONAL + * within 2msec else HC enters RESUME + * + * ... but some hardware won't init fmInterval "by the book" + * (SiS, OPTi ...), so reset again instead. SiS doesn't need + * this if we write fmInterval after we're OPERATIONAL. + * Unclear about ALi, ServerWorks, and others ... this could + * easily be a longstanding bug in chip init on Linux. + */ #if 0 - ehci = struct_ptr(hcd, EHCI_DEV, hcd_interf); - base = ehci->port_base; - - // stop the controller - tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBCMD)); - usbcmd = (PEHCI_USBCMD_CONTENT) & tmp; - usbcmd->run_stop = 0; - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp); - - // wait the controller stop( ehci spec, 16 microframe ) - usb_wait_ms_dpc(2); - - // reset the controller - usbcmd = (PEHCI_USBCMD_CONTENT) & tmp; - usbcmd->hcreset = TRUE; - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp); - - for(;;) - { - // interval.QuadPart = -100 * 10000; // 10 ms - // KeDelayExecutionThread( KernelMode, FALSE, &interval ); - KeStallExecutionProcessor(10); - tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBCMD)); - if (!usbcmd->hcreset) - break; - } - - // prepare the registers - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_CTRLDSSEGMENT), 0); - - // turn on all the int - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBINTR), - EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR - // EHCI_USBINTR_FLROVR | \ // it is noisy - // EHCI_USBINTR_PC // we detect it by polling - ); - // write the list base reg - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_PERIODICLISTBASE), ehci->frame_list_phys_addr.LowPart); - - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_ASYNCLISTBASE), ehci->skel_async_qh->phys_addr & ~(0x1f)); - - usbcmd->int_threshold = 1; - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp); - - // let's rock - usbcmd->run_stop = 1; - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp); - - // set the configuration flag - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_CONFIGFLAG), 1); - - // enable the list traversaling - usbcmd->async_enable = 1; - usbcmd->periodic_enable = 1; - EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp); + if (ohci->flags & OHCI_QUIRK_INITRESET) { + ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); + // flush those writes + (void) ohci_readl (ohci, &ohci->regs->control); + } #endif + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->ed_controlhead, 0); + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->ed_bulkhead, 0); + + /* a reset clears this */ + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->hcca, (ULONG)ohci->hcca_logic_addr.LowPart); + + //periodic_reinit (ohci); + + /* start controller operations */ + hc_control &= OHCI_CTRL_RWC; + hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->control, hc_control); + //ohci_to_hcd(ohci)->state = HC_STATE_RUNNING; + + /* wake on ConnectStatusChange, matching external hubs */ + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->roothub.status, RH_HS_DRWE); + + /* Choose the interrupts we care about now, others later on demand */ + mask = OHCI_INTR_INIT; + //OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->intrstatus, mask); + //OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->intrenable, mask); + + /* handle root hub init quirks ... */ + temp = roothub_a(ohci); + temp &= ~(RH_A_PSM | RH_A_OCPM); + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->roothub.status, RH_HS_LPSC); + OHCI_WRITE_PORT_ULONG((PULONG)&ohci->regs->roothub.b, (temp & RH_A_NPS) ? 0 : RH_B_PPCM); + // flush those writes + (VOID)OHCI_READ_PORT_ULONG((PULONG)&ohci->regs->control); + + //spin_unlock_irq (&ohci->lock); + + // POTPGT delay is bits 24-31, in 2 ms units. + ohci_wait_ms(ohci, (temp >> 23) & 0x1fe); + //ohci_to_hcd(ohci)->state = HC_STATE_RUNNING; + + return TRUE; } @@ -607,21 +709,49 @@ ohci_rh_reset_port(PHCD hcd, UCHAR port_idx) //ULONG i; POHCI_DEV ohci; //ULONG status; - //UCHAR port_count; if (hcd == NULL) return FALSE; ohci = ohci_from_hcd(hcd); - //port_count = (UCHAR) ((PEHCI_HCS_CONTENT) & ehci->ehci_caps.hcs_params)->port_count; - //if (port_idx < 1 || port_idx > port_count) - // return FALSE; + //if (port_idx < 1 || port_idx > ohci->num_ports) + // return FALSE; //usb_dbg_print(DBGLVL_MAXIMUM, ("ohci_rh_reset_port(): status after written=0x%x\n", status)); return TRUE; } +BOOLEAN +ohci_rh_get_dev_change(PHCD hcd, PBYTE buf) //must have the rh dev_lock acquired +{ + POHCI_DEV ohci; + LONG i; + ULONG status; + + if (hcd == NULL) + return FALSE; + + ohci = ohci_from_hcd(hcd); + + for(i = 0; i < ohci->num_ports; i++) + { + status = OHCI_READ_PORT_ULONG((PULONG)(ohci->regs->roothub.portstatus[i])); + + if (status != 0) + { + ohci_dbg_print(DBGLVL_MAXIMUM, ("ohci_rh_get_dev_change(): erh port%d status=0x%x\n", i, status)); + } + + if (status & (RH_PS_PESC | RH_PS_CSC | RH_PS_OCIC)) + { + buf[(i + 1) >> 3] |= (1 << ((i + 1) & 7)); + } + } + return TRUE; +} + + NTSTATUS ohci_hcd_dispatch(PHCD hcd, LONG disp_code, PVOID param) { @@ -630,24 +760,24 @@ ohci_hcd_dispatch(PHCD hcd, LONG disp_code, PVOID param) if (hcd == NULL) return STATUS_INVALID_PARAMETER; ohci = ohci_from_hcd(hcd); -#if 0 + switch (disp_code) { case HCD_DISP_READ_PORT_COUNT: { if (param == NULL) return STATUS_INVALID_PARAMETER; - *((PUCHAR) param) = (UCHAR) HCS_N_PORTS(ehci->ehci_caps.hcs_params); + *((PUCHAR) param) = ohci->num_ports; return STATUS_SUCCESS; } case HCD_DISP_READ_RH_DEV_CHANGE: { - if (ehci_rh_get_dev_change(hcd, param) == FALSE) + if (ohci_rh_get_dev_change(hcd, param) == FALSE) return STATUS_INVALID_PARAMETER; return STATUS_SUCCESS; } } -#endif + return STATUS_NOT_IMPLEMENTED; } diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ohci.h b/reactos/drivers/usb/nt4compat/usbdriver/ohci.h index 037c44875bd..9c7f57b93f3 100644 --- a/reactos/drivers/usb/nt4compat/usbdriver/ohci.h +++ b/reactos/drivers/usb/nt4compat/usbdriver/ohci.h @@ -114,6 +114,146 @@ #define RH_A_NOCP (1 << 12) /* no over current protection */ #define RH_A_POTPGT (0xff << 24) /* power on to power good time */ +/* + * OHCI Endpoint Descriptor (ED) ... holds TD queue + * See OHCI spec, section 4.2 + * + * This is a "Queue Head" for those transfers, which is why + * both EHCI and UHCI call similar structures a "QH". + */ +typedef struct _OHCI_ED { + /* first fields are hardware-specified */ + ULONG hwINFO; /* endpoint config bitmap */ + /* info bits defined by hcd */ +#define ED_DEQUEUE (1 << 27) + /* info bits defined by the hardware */ +#define ED_ISO (1 << 15) +#define ED_SKIP (1 << 14) +#define ED_LOWSPEED (1 << 13) +#define ED_OUT (0x01 << 11) +#define ED_IN (0x02 << 11) + ULONG hwTailP; /* tail of TD list */ + ULONG hwHeadP; /* head of TD list (hc r/w) */ +#define ED_C (0x02) /* toggle carry */ +#define ED_H (0x01) /* halted */ + ULONG hwNextED; /* next ED in list */ + + /* rest are purely for the driver's use */ +#if 0 + dma_addr_t dma; /* addr of ED */ + struct _OHCI_TD *dummy; /* next TD to activate */ + + /* host's view of schedule */ + struct _OHCI_ED *ed_next; /* on schedule or rm_list */ + struct _OHCI_ED *ed_prev; /* for non-interrupt EDs */ + struct list_head td_list; /* "shadow list" of our TDs */ + + /* create --> IDLE --> OPER --> ... --> IDLE --> destroy + * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... + */ + UCHAR state; /* ED_{IDLE,UNLINK,OPER} */ +#define ED_IDLE 0x00 /* NOT linked to HC */ +#define ED_UNLINK 0x01 /* being unlinked from hc */ +#define ED_OPER 0x02 /* IS linked to hc */ + + UCHAR type; /* PIPE_{BULK,...} */ + + /* periodic scheduling params (for intr and iso) */ + UCHAR branch; + USHORT interval; + USHORT load; + USHORT last_iso; /* iso only */ + + /* HC may see EDs on rm_list until next frame (frame_no == tick) */ + USHORT tick; +#endif +} OHCI_ED, *POHCI_ED; + +#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */ + + +/* + * OHCI Transfer Descriptor (TD) ... one per transfer segment + * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt) + * and 4.3.2 (iso) + */ +typedef struct _OHCI_TD { + /* first fields are hardware-specified */ + ULONG hwINFO; /* transfer info bitmask */ + + /* hwINFO bits for both general and iso tds: */ +#define TD_CC 0xf0000000 /* condition code */ +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_DI 0x00E00000 /* frames before interrupt */ +#define TD_DI_SET(X) (((X) & 0x07)<< 21) + /* these two bits are available for definition/use by HCDs in both + * general and iso tds ... others are available for only one type + */ +#define TD_DONE 0x00020000 /* retired to donelist */ +#define TD_ISO 0x00010000 /* copy of ED_ISO */ + + /* hwINFO bits for general tds: */ +#define TD_EC 0x0C000000 /* error count */ +#define TD_T 0x03000000 /* data toggle state */ +#define TD_T_DATA0 0x02000000 /* DATA0 */ +#define TD_T_DATA1 0x03000000 /* DATA1 */ +#define TD_T_TOGGLE 0x00000000 /* uses ED_C */ +#define TD_DP 0x00180000 /* direction/pid */ +#define TD_DP_SETUP 0x00000000 /* SETUP pid */ +#define TD_DP_IN 0x00100000 /* IN pid */ +#define TD_DP_OUT 0x00080000 /* OUT pid */ + /* 0x00180000 rsvd */ +#define TD_R 0x00040000 /* round: short packets OK? */ + + /* (no hwINFO #defines yet for iso tds) */ + + ULONG hwCBP; /* Current Buffer Pointer (or 0) */ + ULONG hwNextTD; /* Next TD Pointer */ + ULONG hwBE; /* Memory Buffer End Pointer */ + + /* PSW is only for ISO. Only 1 PSW entry is used, but on + * big-endian PPC hardware that's the second entry. + */ +#define MAXPSW 2 + USHORT hwPSW [MAXPSW]; + + /* rest are purely for the driver's use */ +#if 0 + UCHAR index; + struct ed *ed; + struct td *td_hash; /* dma-->td hashtable */ + struct td *next_dl_td; + struct urb *urb; + + dma_addr_t td_dma; /* addr of this TD */ + dma_addr_t data_dma; /* addr of data it points to */ + + struct list_head td_list; /* "shadow list", TDs on same ED */ +#endif +} OHCI_TD, *POHCI_TD; + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined section 4.4.1 of the OHCI spec. The HC is + * told the base address of it. It must be 256-byte aligned. + */ +typedef struct _OHCI_HCCA +{ +#define NUM_INTS 32 + ULONG int_table [NUM_INTS]; /* periodic schedule */ + + /* + * OHCI defines u16 frame_no, followed by u16 zero pad. + * Since some processors can't do 16 bit bus accesses, + * portable access must be a 32 bits wide. + */ + ULONG frame_no; /* current frame number */ + ULONG done_head; /* info returned for an interrupt */ + UCHAR reserved_for_hc [116]; + UCHAR what [4]; /* spec only identifies 252 bytes :) */ +} OHCI_HCCA, *POHCI_HCCA; + /* * This is the structure of the OHCI controller's memory mapped I/O region. * You must use readl() and writel() (in ) to access these fields!! @@ -165,6 +305,13 @@ typedef struct _OHCI_DEV BOOLEAN port_mapped; PBYTE port_base; // note: added by ehci_caps.length, operational regs base addr, not the actural base struct _OHCI_REGS *regs; + struct _OHCI_HCCA *hcca; + PVOID td_cache; + PVOID ed_cache; + + PHYSICAL_ADDRESS hcca_logic_addr; + PHYSICAL_ADDRESS td_logic_addr; + PHYSICAL_ADDRESS ed_logic_addr; USHORT num_ports;