|
|
/*++
Copyright (c) 1999, 2000 Microsoft Corporation
Module Name:
hydramp.c
Abstract:
USB 2.0 EHCI driver
Environment:
kernel mode only
Notes:
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
Copyright (c) 1999, 2000 Microsoft Corporation. All Rights Reserved.
Revision History:
2-19-99 : created, jdunn
--*/
#include "common.h"
#include <initguid.h>
#include "usbpriv.h"
//implements the following miniport functions:
//EHCI_StartController
//EHCI_StopController
//EHCI_DisableInterrupts
//EHCI_EnableInterrupts
USB_MINIPORT_STATUS EHCI_InitializeHardware( PDEVICE_DATA DeviceData ) /*++
Routine Description:
Initializes the hardware registers for the host controller.
Arguments:
Return Value:
--*/ { PHC_OPERATIONAL_REGISTER hcOp; PHC_CAPABILITIES_REGISTER hcCap; USBCMD cmd; HCSPARAMS hcSparms; LARGE_INTEGER finishTime, currentTime;
hcCap = DeviceData->CapabilitiesRegisters; hcOp = DeviceData->OperationalRegisters;
// reset the controller
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); LOGENTRY(DeviceData, G, '_res', cmd.ul, 0, 0); cmd.HostControllerReset = 1; WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
KeQuerySystemTime(&finishTime); // no spec'ed time -- we will graciously grant 0.1 sec.
//
// figure when we quit (.1 seconds later)
finishTime.QuadPart += 1000000;
// wait for reset bit to got to zero
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); while (cmd.HostControllerReset) {
KeQuerySystemTime(¤tTime);
if (currentTime.QuadPart >= finishTime.QuadPart) {
EHCI_KdPrint((DeviceData, 0, "'EHCI controller failed to reset in .1 sec!\n"));
return USBMP_STATUS_HARDWARE_FAILURE; }
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
}
hcSparms.ul = READ_REGISTER_ULONG(&hcCap->HcStructuralParameters.ul);
DeviceData->NumberOfPorts = (USHORT) hcSparms.NumberOfPorts;
DeviceData->PortPowerControl = (USHORT) hcSparms.PortPowerControl;
// inialize operational registers
WRITE_REGISTER_ULONG(&hcOp->AsyncListAddr, 0); WRITE_REGISTER_ULONG(&hcOp->PeriodicListBase, 0);
// set the enabled interrupts cache, we'll enable
// these interrupts when asked
DeviceData->EnabledInterrupts.UsbInterrupt = 1; DeviceData->EnabledInterrupts.UsbError = 1; DeviceData->EnabledInterrupts.FrameListRollover = 1; DeviceData->EnabledInterrupts.HostSystemError = 1; DeviceData->EnabledInterrupts.IntOnAsyncAdvance = 1;
return USBMP_STATUS_SUCCESS; }
USB_MINIPORT_STATUS EHCI_InitializeSchedule( PDEVICE_DATA DeviceData, PUCHAR StaticQHs, HW_32BIT_PHYSICAL_ADDRESS StaticQHsPhys ) /*++
Routine Description:
Build the schedule of static Eds
Arguments:
Return Value:
--*/ { USB_MINIPORT_STATUS mpStatus; ULONG length; ULONG i; PHC_OPERATIONAL_REGISTER hcOp; PHCD_QUEUEHEAD_DESCRIPTOR qh; HW_LINK_POINTER asyncHwQh; PHW_32BIT_PHYSICAL_ADDRESS frameBase;
hcOp = DeviceData->OperationalRegisters;
// allocate a frame list
frameBase = DeviceData->FrameListBaseAddress = (PHW_32BIT_PHYSICAL_ADDRESS) StaticQHs; DeviceData->FrameListBasePhys = StaticQHsPhys; StaticQHs += USBEHCI_MAX_FRAME*sizeof(HW_32BIT_PHYSICAL_ADDRESS); StaticQHsPhys += USBEHCI_MAX_FRAME*sizeof(HW_32BIT_PHYSICAL_ADDRESS);
// allocate a 'Dummy' QH for the Async list
qh = (PHCD_QUEUEHEAD_DESCRIPTOR) StaticQHs;
RtlZeroMemory(qh, sizeof(*qh)); asyncHwQh.HwAddress = qh->PhysicalAddress = StaticQHsPhys; // no current TD
// t-bit set on next TD
SET_T_BIT(qh->HwQH.Overlay.qTD.Next_qTD.HwAddress); qh->HwQH.Overlay.qTD.Token.Halted = 1; qh->HwQH.EpChars.HeadOfReclimationList = 1; qh->Sig = SIG_HCD_AQH; SET_QH(asyncHwQh.HwAddress); // link to ourselves
qh->HwQH.HLink.HwAddress = asyncHwQh.HwAddress; QH_DESCRIPTOR_PTR(qh->NextQh) = qh; QH_DESCRIPTOR_PTR(qh->PrevQh) = qh;
DeviceData->AsyncQueueHead = qh;
StaticQHs += sizeof(HCD_QUEUEHEAD_DESCRIPTOR); StaticQHsPhys += sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
// allocate 64 static interrupt queue heads
for (i=0; i<64; i++) { qh = (PHCD_QUEUEHEAD_DESCRIPTOR) StaticQHs; qh->PhysicalAddress = StaticQHsPhys;
DeviceData->StaticInterruptQH[i] = qh;
StaticQHs += sizeof(HCD_QUEUEHEAD_DESCRIPTOR); StaticQHsPhys += sizeof(HCD_QUEUEHEAD_DESCRIPTOR); }
EHCI_InitailizeInterruptSchedule(DeviceData);
for (i=0; i<USBEHCI_MAX_FRAME; i++) {
PHCD_QUEUEHEAD_DESCRIPTOR qh; HW_32BIT_PHYSICAL_ADDRESS qhPhys;
qh = EHCI_GetQueueHeadForFrame(DeviceData, i);
qhPhys = qh->PhysicalAddress; SET_QH(qhPhys);
*frameBase = qhPhys; frameBase++; }
DeviceData->DummyQueueHeads = StaticQHs; DeviceData->DummyQueueHeadsPhys = StaticQHsPhys;
StaticQHs+= sizeof(HCD_QUEUEHEAD_DESCRIPTOR)*USBEHCI_MAX_FRAME; StaticQHsPhys+= sizeof(HCD_QUEUEHEAD_DESCRIPTOR)*USBEHCI_MAX_FRAME;
EHCI_AddDummyQueueHeads(DeviceData);
// program the frame list
WRITE_REGISTER_ULONG(&hcOp->PeriodicListBase, DeviceData->FrameListBasePhys);
// write the async qh to the controller
WRITE_REGISTER_ULONG(&hcOp->AsyncListAddr, asyncHwQh.HwAddress);
mpStatus = USBMP_STATUS_SUCCESS;
return mpStatus; }
VOID EHCI_ReadUlongRegFlag( PDEVICE_DATA DeviceData, PUCHAR DebugString, PWCHAR FlagName, ULONG FlagNameSize, ULONG FlagBit ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { USB_MINIPORT_STATUS mpStatus; ULONG flag;
// get SOF modify value from registry
mpStatus = USBPORT_GET_REGISTRY_KEY_VALUE(DeviceData, TRUE, // software branch
FlagName, FlagNameSize, &flag, sizeof(flag));
// if this call fails we just use the default
if (mpStatus == USBMP_STATUS_SUCCESS) {
if (flag) { SET_FLAG(DeviceData->Flags, FlagBit); } EHCI_KdPrint((DeviceData, 1, "'%s: %d \n", DebugString, flag));
} }
VOID EHCI_GetRegistryParameters( PDEVICE_DATA DeviceData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ {
EHCI_ReadUlongRegFlag(DeviceData, "Enable Idle Endpoint Support", EN_IDLE_ENDPOINT_SUPPORT, sizeof(EN_IDLE_ENDPOINT_SUPPORT), EHCI_DD_EN_IDLE_EP_SUPPORT);
}
VOID USBMPFN EHCI_StopController( PDEVICE_DATA DeviceData, BOOLEAN HwPresent ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { USBCMD cmd; PHC_OPERATIONAL_REGISTER hcOp = NULL; CONFIGFLAG configFlag;
hcOp = DeviceData->OperationalRegisters;
// clear the run bit
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); LOGENTRY(DeviceData, G, '_stp', cmd.ul, 0, 0); cmd.HostControllerRun = 0; WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
// mask off all interrupts
WRITE_REGISTER_ULONG(&hcOp->UsbInterruptEnable.ul, 0);
// set cc control of the hc ports to the companion
// controllers
configFlag.ul = 0; configFlag.RoutePortsToEHCI = 0; WRITE_REGISTER_ULONG(&hcOp->ConfigFlag.ul, configFlag.ul);
}
VOID USBMPFN EHCI_TakePortControl( PDEVICE_DATA DeviceData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { PHC_OPERATIONAL_REGISTER hcOp = NULL; CONFIGFLAG configFlag;
hcOp = DeviceData->OperationalRegisters;
configFlag.ul = READ_REGISTER_ULONG(&hcOp->ConfigFlag.ul); EHCI_KdPrint((DeviceData, 0, "'EHCI - configflag %x\n", configFlag.ul)); DeviceData->LastConfigFlag.ul = configFlag.ul;
// set default port routing
configFlag.ul = 0; configFlag.RoutePortsToEHCI = 1; WRITE_REGISTER_ULONG(&hcOp->ConfigFlag.ul, configFlag.ul);
}
USB_MINIPORT_STATUS USBMPFN EHCI_StartController( PDEVICE_DATA DeviceData, PHC_RESOURCES HcResources ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { USB_MINIPORT_STATUS mpStatus; PHC_OPERATIONAL_REGISTER hcOp = NULL; PHC_CAPABILITIES_REGISTER hcCap = NULL; PUCHAR base; USBCMD cmd; HCLENGTHVERSION hcLengthVersion; ULONG capLength; ULONG hciVersion; CONFIGFLAG configFlag; UCHAR fladj; // fBIOS set frame length adjustment
DeviceData->Sig = SIG_EHCI_DD; DeviceData->ControllerFlavor = HcResources->ControllerFlavor; DeviceData->DeviceStarted = FALSE;
USBPORT_READ_CONFIG_SPACE( DeviceData, &DeviceData->Vid, 0, sizeof(DeviceData->Vid));
USBPORT_READ_CONFIG_SPACE( DeviceData, &DeviceData->Dev, 2, sizeof(DeviceData->Dev));
#if DBG
if (AGERE(DeviceData)) { EHCI_KdPrint((DeviceData, 0, "'EHCI Agere Controller Detected\n")); } else if (NEC(DeviceData)) { EHCI_KdPrint((DeviceData, 0, "'EHCI NEC Controller Detected\n")); } else { EHCI_KdPrint((DeviceData, 0, "'EHCI Generic Controller Detected\n")); } #endif
// get the frame length adjustment value set by the BIOS
USBPORT_READ_CONFIG_SPACE( DeviceData, &fladj, 0x61, sizeof(fladj));
DeviceData->SavedFladj = fladj;
DeviceData->IsoEndpointListHead = NULL;
if (EHCI_PastExpirationDate(DeviceData)) { return USBMP_STATUS_INIT_FAILURE; }
// assume success
mpStatus = USBMP_STATUS_SUCCESS;
EHCI_ASSERT(DeviceData, HcResources->CommonBufferVa != NULL); // validate our resources
if ((HcResources->Flags & (HCR_MEM_REGS | HCR_IRQ)) != (HCR_MEM_REGS | HCR_IRQ)) { mpStatus = USBMP_STATUS_INIT_FAILURE; }
base = HcResources->DeviceRegisters;
hcCap = DeviceData->CapabilitiesRegisters = (PHC_CAPABILITIES_REGISTER) base;
hcLengthVersion.ul = READ_REGISTER_ULONG(&hcCap->HcLengthVersion.ul);
capLength = hcLengthVersion.HcCapLength; hciVersion = hcLengthVersion.HcVersion;
EHCI_KdPrint((DeviceData, 1, "'EHCI CAPLENGTH = 0x%x\n", capLength)); EHCI_KdPrint((DeviceData, 1, "'EHCI HCIVERSION = 0x%x\n", hciVersion));
// set up or device data structure
hcOp = DeviceData->OperationalRegisters = (PHC_OPERATIONAL_REGISTER) (base + capLength);
EHCI_KdPrint((DeviceData, 1, "'EHCI mapped Operational Regs = %x\n", hcOp)); EHCI_KdPrint((DeviceData, 1, "'EHCI mapped Capabilities Regs = %x\n", hcCap));
EHCI_GetRegistryParameters(DeviceData);
// if (mpStatus == USBMP_STATUS_SUCCESS) {
// mpStatus = EHCI_StopBIOS(DeviceData);
// }
if (mpStatus == USBMP_STATUS_SUCCESS) { // got resources and schedule
// init the controller
mpStatus = EHCI_InitializeHardware(DeviceData); }
if (mpStatus == USBMP_STATUS_SUCCESS) {
// inialize static Queue Heads
PUCHAR staticQHs; HW_32BIT_PHYSICAL_ADDRESS staticQHsPhys;
// carve the common buffer block in to
// static QueueHeads
//
// set up the schedule
staticQHs = HcResources->CommonBufferVa; staticQHsPhys = HcResources->CommonBufferPhys;
// set up the schedule
mpStatus = EHCI_InitializeSchedule(DeviceData, staticQHs, staticQHsPhys);
}
if (mpStatus == USBMP_STATUS_SUCCESS) {
USBPORT_READ_CONFIG_SPACE( DeviceData, &fladj, 0x61, sizeof(fladj));
if (fladj != DeviceData->SavedFladj) { TEST_TRAP();
fladj = DeviceData->SavedFladj; USBPORT_WRITE_CONFIG_SPACE( DeviceData, &fladj, 0x61, sizeof(fladj)); }
// set default port routing
configFlag.ul = 0; configFlag.RoutePortsToEHCI = 1; WRITE_REGISTER_ULONG(&hcOp->ConfigFlag.ul, configFlag.ul);
DeviceData->LastConfigFlag.ul = configFlag.ul;
// set the interrupt threshold to maximum
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); cmd.InterruptThreshold = 1; WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
// start the controller
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); LOGENTRY(DeviceData, G, '_run', cmd.ul, 0, 0); cmd.HostControllerRun = 1; WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
DeviceData->DeviceStarted = TRUE;
if (HcResources->Restart) { USHORT p; // we have a restart, re-power the ports here so that
// we can hand of devices that are on the 1.1 bus
EHCI_KdPrint((DeviceData, 0, "'Restart, power chirpable ports\n")); // power the ports
for (p = 1; p <= DeviceData->NumberOfPorts; p++) { EHCI_RH_SetFeaturePortPower(DeviceData, p); }
// no poweron2powergood for EHCI root ports, wait
// 100 ms for port power stabilization
// 100 ms minimum debiunce time
USBPORT_WAIT(DeviceData, 200);
// bugbug this will keep some HS mass storage devices from failing after
// hibernate, however it will significantly increase resume from hibernate
// time. see bug #586818
// USBPORT_WAIT(DeviceData, 500);
for (p = 1; p <= DeviceData->NumberOfPorts; p++) { EHCI_RH_ChirpRootPort(DeviceData, p); }
}
} else {
DEBUG_BREAK(DeviceData); }
return mpStatus; }
VOID EHCI_SuspendController( PDEVICE_DATA DeviceData ) { USBCMD cmd; USBSTS status; USBINTR intr; PHC_OPERATIONAL_REGISTER hcOp = NULL; ULONG i,p; USBSTS irqStatus;
hcOp = DeviceData->OperationalRegisters; // save all volatile regs from the core power well
// since we may loose power on the controller chip (not bus)
// the miniport is resposnible for saving HW state
DeviceData->PeriodicListBaseSave = READ_REGISTER_ULONG(&hcOp->PeriodicListBase); DeviceData->AsyncListAddrSave = READ_REGISTER_ULONG(&hcOp->AsyncListAddr); DeviceData->SegmentSelectorSave = READ_REGISTER_ULONG(&hcOp->SegmentSelector); // preserve the state of the list enable bits
DeviceData->CmdSave.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
// reset the PM chirp state flags for another pass at power
// management
DeviceData, DeviceData->PortPMChirp == 0;
// Save away the command register
// DeviceData->SuspendCommandReg.us =
// command.us = READ_PORT_USHORT(®->UsbCommand.us);
// clear the int on async advance doorbell
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); cmd.IntOnAsyncAdvanceDoorbell = 0; WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
// Stop the controller
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); LOGENTRY(DeviceData, G, '_st1', cmd.ul, 0, 0); cmd.HostControllerRun = 0; WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
// ack any interrupts that may be left over from the halt
// process. The controller should not generate any new
// interrupts when it is stopped. For some reason the NEC
// controller generates a doorbel interrupt on halt.
// wait 1 microframe
KeStallExecutionProcessor(125); irqStatus.ul = READ_REGISTER_ULONG(&hcOp->UsbStatus.ul); // just look at the IRQ status bits
irqStatus.ul &= HcInterruptStatusMask; if (irqStatus.ul != 0) { WRITE_REGISTER_ULONG(&hcOp->UsbStatus.ul, irqStatus.ul); }
// mask off all interrupts now
WRITE_REGISTER_ULONG(&hcOp->UsbInterruptEnable.ul, 0);
// Wait for the HC to halt
// Note that according to the sepc if we don't halt in ms
// (16ms) the hardware is busted.
for (i=0; i<10; i++) { status.ul = READ_REGISTER_ULONG(&hcOp->UsbStatus.ul); if (status.HcHalted) { break; } USBPORT_WAIT(DeviceData, 1); }
if (status.HcHalted != 1) { // hardware is f'ed up
TEST_TRAP(); }
//if (!status.HCHalted) {
//
// // Can't get the HCHalted bit to stick, so reset the controller.
// command.GlobalReset = 1;
// WRITE_PORT_USHORT(®->UsbCommand.us, command.us);
//
// USBPORT_WAIT(DeviceData, 10);
//
// command.GlobalReset = 0;
// WRITE_PORT_USHORT(®->UsbCommand.us, command.us);
// // Re-enable interrupts, since they are zero'd out on reset.
// WRITE_PORT_USHORT(®->UsbInterruptEnable.us, DeviceData->EnabledInterrupts.us);
//
//}
// enable the port chage interrupt, this allows us to wake
// in the selective suspend case
intr.ul = READ_REGISTER_ULONG(&hcOp->UsbInterruptEnable.ul); intr.PortChangeDetect = 1; WRITE_REGISTER_ULONG(&hcOp->UsbInterruptEnable.ul, intr.ul); }
USB_MINIPORT_STATUS EHCI_ResumeController( PDEVICE_DATA DeviceData ) { USBCMD cmd; PHC_OPERATIONAL_REGISTER hcOp = NULL; CONFIGFLAG configFlag;
hcOp = DeviceData->OperationalRegisters;
EHCI_KdPrint((DeviceData, 1, "'>EHCI_ResumeController\n"));
// don't mess with handoff regs for now
//configFlag.ul = 0;
//configFlag.RoutePortsToEHCI = 1;
//WRITE_REGISTER_ULONG(&hcOp->ConfigFlag.ul, configFlag.ul);
// restore volitile regs
//configFlag.ul = READ_REGISTER_ULONG(&hcOp->ConfigFlag.ul);
configFlag.ul = DeviceData->LastConfigFlag.ul; if (configFlag.RoutePortsToEHCI == 0) { // we have a reset
EHCI_KdPrint((DeviceData, 1, "'Routing bit has reset to 0\n"));
configFlag.RoutePortsToEHCI = 1; DeviceData->LastConfigFlag.ul = configFlag.ul; WRITE_REGISTER_ULONG(&hcOp->ConfigFlag.ul, configFlag.ul);
return USBMP_STATUS_HARDWARE_FAILURE; }
// restore volitile regs
WRITE_REGISTER_ULONG(&hcOp->SegmentSelector, DeviceData->SegmentSelectorSave); WRITE_REGISTER_ULONG(&hcOp->PeriodicListBase, DeviceData->PeriodicListBaseSave); WRITE_REGISTER_ULONG(&hcOp->AsyncListAddr, DeviceData->AsyncListAddrSave);
// start the controller
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul); LOGENTRY(DeviceData, G, '_run', cmd.ul, 0, 0); cmd.HostControllerRun = 1;
// restore volatile cmd bits
cmd.AsyncScheduleEnable = DeviceData->CmdSave.AsyncScheduleEnable; cmd.PeriodicScheduleEnable = DeviceData->CmdSave.PeriodicScheduleEnable; cmd.InterruptThreshold = DeviceData->CmdSave.InterruptThreshold;
WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul, cmd.ul);
WRITE_REGISTER_ULONG(&hcOp->UsbInterruptEnable.ul, DeviceData->EnabledInterrupts.ul);
EHCI_KdPrint((DeviceData, 1, "'<EHCI_ResumeController\n"));
return USBMP_STATUS_SUCCESS; }
USB_MINIPORT_STATUS EHCI_OpenEndpoint( PDEVICE_DATA DeviceData, PENDPOINT_PARAMETERS EndpointParameters, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { USB_MINIPORT_STATUS mpStatus;
EndpointData->Sig = SIG_EP_DATA; // save a copy of the parameters
EndpointData->Parameters = *EndpointParameters; EndpointData->Flags = 0; EndpointData->PendingTransfers = 0;
switch (EndpointParameters->TransferType) {
case Control:
EndpointData->MaxPendingTransfers = 1; mpStatus = EHCI_OpenBulkOrControlEndpoint( DeviceData, TRUE, EndpointParameters, EndpointData);
break;
case Interrupt:
mpStatus = EHCI_OpenInterruptEndpoint( DeviceData, EndpointParameters, EndpointData);
break;
case Bulk:
EndpointData->MaxPendingTransfers = 1; mpStatus = EHCI_OpenBulkOrControlEndpoint( DeviceData, FALSE, EndpointParameters, EndpointData);
break;
case Isochronous:
if (EndpointParameters->DeviceSpeed == HighSpeed) { mpStatus = EHCI_OpenHsIsochronousEndpoint( DeviceData, EndpointParameters, EndpointData); } else { mpStatus = EHCI_OpenIsochronousEndpoint( DeviceData, EndpointParameters, EndpointData); } break;
default: TEST_TRAP(); mpStatus = USBMP_STATUS_NOT_SUPPORTED; }
return mpStatus; }
VOID EHCI_CloseEndpoint( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { // nothing to do here
}
USB_MINIPORT_STATUS EHCI_PokeEndpoint( PDEVICE_DATA DeviceData, PENDPOINT_PARAMETERS EndpointParameters, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { PHCD_QUEUEHEAD_DESCRIPTOR qh; ULONG oldBandwidth;
LOGENTRY(DeviceData, G, '_Pok', EndpointData, EndpointParameters, 0);
switch(EndpointData->Parameters.TransferType) { case Interrupt: case Control: case Bulk: return EHCI_PokeAsyncEndpoint(DeviceData, EndpointParameters, EndpointData); case Isochronous: return EHCI_PokeIsoEndpoint(DeviceData, EndpointParameters, EndpointData); }
return USBMP_STATUS_SUCCESS; }
VOID EHCI_RebalanceEndpoint( PDEVICE_DATA DeviceData, PENDPOINT_PARAMETERS EndpointParameters, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
compute how much common buffer we will need for this endpoint
Arguments:
Return Value:
--*/ { switch (EndpointParameters->TransferType) { case Interrupt: EHCI_RebalanceInterruptEndpoint(DeviceData, EndpointParameters, EndpointData); break;
case Isochronous: EHCI_RebalanceIsoEndpoint(DeviceData, EndpointParameters, EndpointData); break; } }
USB_MINIPORT_STATUS EHCI_QueryEndpointRequirements( PDEVICE_DATA DeviceData, PENDPOINT_PARAMETERS EndpointParameters, OUT PENDPOINT_REQUIREMENTS EndpointRequirements ) /*++
Routine Description:
compute how much common buffer we will need for this endpoint
Arguments:
Return Value:
--*/ {
switch (EndpointParameters->TransferType) {
case Control:
EndpointRequirements->MinCommonBufferBytes = sizeof(HCD_QUEUEHEAD_DESCRIPTOR) + TDS_PER_CONTROL_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
EndpointRequirements->MaximumTransferSize = MAX_CONTROL_TRANSFER_SIZE; break;
case Interrupt:
EndpointRequirements->MinCommonBufferBytes = sizeof(HCD_QUEUEHEAD_DESCRIPTOR) + TDS_PER_INTERRUPT_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
EndpointRequirements->MaximumTransferSize = MAX_INTERRUPT_TRANSFER_SIZE;
break;
case Bulk:
//
// TDS_PER_ENDPOINT limits the largest transfer we
// can handle.
//
// TDS_PER_ENDPOINT TDs plus an ED
EndpointRequirements->MinCommonBufferBytes = sizeof(HCD_QUEUEHEAD_DESCRIPTOR) + TDS_PER_BULK_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
EndpointRequirements->MaximumTransferSize = MAX_BULK_TRANSFER_SIZE; break;
case Isochronous:
if (EndpointParameters->DeviceSpeed == HighSpeed) { EndpointRequirements->MinCommonBufferBytes = USBEHCI_MAX_FRAME*sizeof(HCD_HSISO_TRANSFER_DESCRIPTOR);
EndpointRequirements->MaximumTransferSize = MAX_HSISO_TRANSFER_SIZE; } else { // TDS_PER_ENDPOINT TDs plus an ED
EndpointRequirements->MinCommonBufferBytes = TDS_PER_ISO_ENDPOINT*sizeof(HCD_SI_TRANSFER_DESCRIPTOR);
EndpointRequirements->MaximumTransferSize = MAX_ISO_TRANSFER_SIZE; } break;
default: USBPORT_BUGCHECK(DeviceData); }
return USBMP_STATUS_SUCCESS; }
VOID EHCI_PollEndpoint( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Arguments:
Return Value:
--*/
{ switch(EndpointData->Parameters.TransferType) {
case Control: case Bulk: case Interrupt: EHCI_PollAsyncEndpoint(DeviceData, EndpointData); break; case Isochronous: EHCI_PollIsoEndpoint(DeviceData, EndpointData); break; } }
PHCD_TRANSFER_DESCRIPTOR EHCI_AllocTd( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Allocate a TD from an endpoints pool
Arguments:
Return Value:
--*/ { ULONG i; PHCD_TRANSFER_DESCRIPTOR td;
for (i=0; i<EndpointData->TdCount; i++) { td = &EndpointData->TdList->Td[i];
if (!TEST_FLAG(td->Flags, TD_FLAG_BUSY)) { SET_FLAG(td->Flags, TD_FLAG_BUSY); LOGENTRY(DeviceData, G, '_aTD', td, 0, 0); EndpointData->FreeTds--; return td; } }
// we should always find one
EHCI_ASSERT(DeviceData, FALSE); USBPORT_BUGCHECK(DeviceData);
return NULL; }
VOID EHCI_SetEndpointStatus( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, MP_ENDPOINT_STATUS Status ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { switch (EndpointData->Parameters.TransferType) { case Control: case Bulk: case Interrupt: EHCI_SetAsyncEndpointStatus(DeviceData, EndpointData, Status); break; case Isochronous: // nothing to do for iso
break;
default: USBPORT_BUGCHECK(DeviceData); } }
MP_ENDPOINT_STATUS EHCI_GetEndpointStatus( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { switch (EndpointData->Parameters.TransferType) { case Control: case Bulk: case Interrupt: return EHCI_GetAsyncEndpointStatus(DeviceData, EndpointData); break; }
// return RUNNING for iso
return ENDPOINT_STATUS_RUN; }
VOID EHCI_SetEndpointState( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, MP_ENDPOINT_STATE State ) /*++
Routine Description:
Arguments:
Return Value:
--*/ {
LOGENTRY(DeviceData, G, '_ses', EndpointData, 0, State);
switch (EndpointData->Parameters.TransferType) { case Control: case Bulk: case Interrupt: EHCI_SetAsyncEndpointState(DeviceData, EndpointData, State); break; case Isochronous: EHCI_SetIsoEndpointState(DeviceData, EndpointData, State); break; default: USBPORT_BUGCHECK(DeviceData); }
}
MP_ENDPOINT_STATE EHCI_GetEndpointState( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { MP_ENDPOINT_STATE currentState; PHCD_QUEUEHEAD_DESCRIPTOR qh;
// assume we are active
currentState = ENDPOINT_ACTIVE;
qh = EndpointData->QueueHead;
// removed from schedule?
if (!TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE)) { // yes
currentState = TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_QH_REMOVED) ? ENDPOINT_REMOVE : ENDPOINT_PAUSE; }
LOGENTRY(DeviceData, G, '_ges', EndpointData, 0, currentState);
return currentState; }
VOID EHCI_PollController( PDEVICE_DATA DeviceData ) /*++
Routine Description:
Arguments:
Return Value:
--*/ {
USBPORT_INVALIDATE_ROOTHUB(DeviceData); }
USB_MINIPORT_STATUS EHCI_SubmitTransfer( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, PTRANSFER_PARAMETERS TransferParameters, PTRANSFER_CONTEXT TransferContext, PTRANSFER_SG_LIST TransferSGList ) { USB_MINIPORT_STATUS mpStatus;
// init the context
RtlZeroMemory(TransferContext, sizeof(*TransferContext)); TransferContext->Sig = SIG_EHCI_TRANSFER; TransferContext->UsbdStatus = USBD_STATUS_SUCCESS; TransferContext->EndpointData = EndpointData; TransferContext->TransferParameters = TransferParameters;
switch (EndpointData->Parameters.TransferType) { case Control: mpStatus = EHCI_ControlTransfer(DeviceData, EndpointData, TransferParameters, TransferContext, TransferSGList); break; case Interrupt: mpStatus = EHCI_InterruptTransfer(DeviceData, EndpointData, TransferParameters, TransferContext, TransferSGList); break; case Bulk: mpStatus = EHCI_BulkTransfer(DeviceData, EndpointData, TransferParameters, TransferContext, TransferSGList); break; default: TEST_TRAP(); }
return mpStatus; }
VOID EHCI_AbortTransfer( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, PTRANSFER_CONTEXT TransferContext, OUT PULONG BytesTransferred ) { switch (EndpointData->Parameters.TransferType) { case Control: case Interrupt: case Bulk: EHCI_AbortAsyncTransfer(DeviceData, EndpointData, TransferContext); break; default: EHCI_AbortIsoTransfer(DeviceData, EndpointData, TransferContext); } }
USB_MINIPORT_STATUS EHCI_PassThru ( PDEVICE_DATA DeviceData, GUID *FunctionGuid, ULONG ParameterLength, OUT PVOID Parameters ) { PUCHAR p = Parameters; UCHAR pdkApi; ULONG portNumber; USB_MINIPORT_STATUS mpStatus;
mpStatus = USBMP_STATUS_NOT_SUPPORTED; if (RtlEqualMemory(FunctionGuid, &GUID_USBPRIV_ROOTPORT_STATUS, sizeof(GUID))) { mpStatus = EHCI_RH_UsbprivRootPortStatus(DeviceData, ParameterLength, Parameters); }
#if 0
portNumber = *(p+1);
mpStatus = USBMP_STATUS_NOT_SUPPORTED;
// pdkApi - force full speed
pdkApi = *p; switch (pdkApi) { // obtumtuserate the port as requested
case 0: { PHC_OPERATIONAL_REGISTER hcOp; USHORT portNumber; PORTSC port;
portNumber = *(p+1); hcOp = DeviceData->OperationalRegisters;
// first power the port up
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul); port.PortPower = 1; WRITE_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul, port.ul);
KeStallExecutionProcessor(10); //stall for 10 microseconds
EHCI_OptumtuseratePort(DeviceData, portNumber);
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
SET_BIT(DeviceData->HighSpeedDeviceAttached, portNumber-1);
// see if it worked
if (port.ul == 0x1205) { mpStatus = USBMP_STATUS_SUCCESS; } else { mpStatus = USBMP_STATUS_FAILURE; }
LOGENTRY(DeviceData, G, '_hsE', portNumber, mpStatus, port.ul); TEST_TRAP(); } break;
case 1: // force a connect change
// indicate a port change condition to the hub
SET_BIT(DeviceData->PortConnectChange, portNumber-1);
break; } #endif
return mpStatus; }
VOID EHCI_SetEndpointDataToggle( PDEVICE_DATA DeviceData, PENDPOINT_DATA EndpointData, ULONG Toggle ) /*++
Routine Description:
Arguments:
Toggle is 0 or 1
Return Value:
--*/
{ PHCD_QUEUEHEAD_DESCRIPTOR qh;
if (EndpointData->Parameters.TransferType == Control || EndpointData->Parameters.TransferType == Isochronous) {
// nothing to do for control and iso
return; }
qh = EndpointData->QueueHead; qh->HwQH.Overlay.qTD.Token.DataToggle = Toggle;
LOGENTRY(DeviceData, G, '_stg', EndpointData, 0, Toggle); }
VOID EHCI_CheckController( PDEVICE_DATA DeviceData ) { if (DeviceData->DeviceStarted) { EHCI_HardwarePresent(DeviceData, TRUE); } }
// Beta versions of our miniport driver have a hard coded exp date
#ifdef NO_EXP_DATE
#define EXPIRATION_DATE 0
#else
//Sep 1, 2001
//#define EXPIRATION_DATE 0x01c133406ab2406c
//Oct 24, 2001
//#define EXPIRATION_DATE 0x01c15cd5887bc884
//Dec 31, 2001
//#define EXPIRATION_DATE 0x01c19251a68bfac0
#endif
BOOLEAN EHCI_PastExpirationDate( PDEVICE_DATA DeviceData ) { LARGE_INTEGER systemTime;
KeQuerySystemTime(&systemTime);
EHCI_KdPrint((DeviceData, 1, "system time: %x %x\n", systemTime.QuadPart)); EHCI_KdPrint((DeviceData, 1, "exp system time: %x %x\n", EXPIRATION_DATE));
if (EXPIRATION_DATE && systemTime.QuadPart > EXPIRATION_DATE) { EHCI_KdPrint((DeviceData, 1, "driver expired")); return TRUE; }
return FALSE; }
UCHAR EHCI_GetEECP( PDEVICE_DATA DeviceData, UCHAR CapabilityId ) /*
returns the offset of a specific EECP in config space given a cap id
returns 0 if no EECP or Id not found
*/ { UCHAR eecpOffset; HC_EECP eecp; PHC_CAPABILITIES_REGISTER hcCap;
hcCap = DeviceData->CapabilitiesRegisters;
eecpOffset = (UCHAR) hcCap->HcCapabilityParameters.EECP;
if (eecpOffset == 0) { return eecpOffset; }
EHCI_KdPrint((DeviceData, 1, "EECP offset found @ 0x%x\n", eecpOffset));
do {
USBPORT_READ_CONFIG_SPACE( DeviceData, &eecp, eecpOffset, sizeof(eecp));
EHCI_KdPrint((DeviceData, 1, "EECP cap 0x%x Next 0x%x (%08.8x)\n", eecp.CapId, eecp.NextCap, eecp.ul));
if (eecp.CapId == CapabilityId) { return eecpOffset; }
eecpOffset = (UCHAR) eecp.NextCap;
} while (eecpOffset);
return eecpOffset; }
USB_MINIPORT_STATUS EHCI_StopBIOS( PDEVICE_DATA DeviceData ) /*++
Routine Description:
Stop Legacy BIOS
Arguments:
Return Value:
--*/ { UCHAR biosOffset; USB_LEGACYBIOS_REGISTERS hcLegs;
biosOffset = EHCI_GetEECP(DeviceData, EECP_PRE_OS_HANDOFF);
// do we have BIOS
if (biosOffset) { // hey! nice Legs.
EHCI_KdPrint((DeviceData, 1, "EHCI Legacy BIOS EECP registers detected\n"));
// read the legacy registers
USBPORT_READ_CONFIG_SPACE(DeviceData, &hcLegs, biosOffset, sizeof(hcLegs));
// see if BIOS is enabled
if (hcLegs.Caps.HcBIOSowned) { USBLEGSUP legSup;
EHCI_KdPrint((DeviceData, 0, "EHCI Legacy BIOS detected\n")); TEST_TRAP();
// take ownership,
// set OS owned
legSup.ul = hcLegs.Caps.ul; legSup.HcOSowned = 1;
USBPORT_WRITE_CONFIG_SPACE(DeviceData, &legSup, biosOffset, sizeof(legSup));
// wait on Bios owned to go to zero
do { USBPORT_READ_CONFIG_SPACE(DeviceData, &legSup, biosOffset, sizeof(legSup)); } while (legSup.HcBIOSowned);
} }
return USBMP_STATUS_SUCCESS; }
|