Author: fireball
Date: Tue Nov 20 23:50:56 2007
New Revision: 30607
URL:
http://svn.reactos.org/svn/reactos?rev=30607&view=rev
Log:
- Implement init and start of OHCI controller (interrupts are still disabled, because
corresponding code is not implemented).
- Implement stubbed HCD interface functions for OHCI.
- Add Endpoint Descriptor and Transfer Descriptor structs from Linux.
Modified:
trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.c
trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.h
Modified: trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/nt4compat/usbd…
==============================================================================
--- trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.c (original)
+++ trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.c Tue Nov 20 23:50:56 2007
@@ -87,6 +87,12 @@
*/
+/* 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)
{
@@ -160,6 +166,28 @@
}
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 @@
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 @@
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;
-
- //if ((ret = ohci_mem_init (ohci)) < 0)
- // ohci_stop (ohci_to_hcd(ohci));
+ pdev_ext->ohci->hcca = HalAllocateCommonBuffer(pdev_ext->padapter,
+ sizeof(*pdev_ext->ohci->hcca), &pdev_ext->ohci->hcca_logic_addr,
FALSE);
+
+ 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 @@
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,20 +709,48 @@
//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 @@
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;
}
Modified: trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/nt4compat/usbd…
==============================================================================
--- trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.h (original)
+++ trunk/reactos/drivers/usb/nt4compat/usbdriver/ohci.h Tue Nov 20 23:50:56 2007
@@ -115,6 +115,146 @@
#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 <asm/io.h>) to access these fields!!
* Layout is in section 7 (and appendix B) of the spec.
@@ -165,6 +305,13 @@
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;