- 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.

svn path=/trunk/; revision=30607
This commit is contained in:
Aleksey Bragin 2007-11-20 20:50:56 +00:00
parent 0fdacaa71e
commit de4fffafa1
2 changed files with 350 additions and 73 deletions

View file

@ -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;
}

View file

@ -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 <asm/io.h>) 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;